Mycat搭建负载均衡,读写分离的Mysql集群

Mycat搭建负载均衡,读写分离的Mysql集群

准备环境

1、mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz

2、Mycat-server-1.6.7.4-test-20191113141017-linux.tar.gz

3、Centos7

运行环境

1个mycat,四个mysql实例(两个一主一从)。

下载安装配置运行

1、mysql安装配置运行

参考我的另一篇随笔:从零开始:mysql基于Amoeba的集群搭建

mysql需要创建3307,3308,3309,3310端口,其中,3307(主)和3308(从),3310(主)和3309(从)。

[root@iz2zeaf5jdjve80rjlsjgnz support-files]# ps -aux | grep mysql
root      5708  0.0  0.0 112712   964 pts/0    R+   23:25   0:00 grep --color=auto mysql
root      6218  0.0  0.0 113320  1608 pts/0    S    22:43   0:00 /bin/sh /software/mysql/mysql3310/bin/mysqld_safe --datadir=/software/mysql/mysql3310/data --pid-file=/software/mysql/mysql3310/mysqld.pid
mysql     7008  0.0 14.5 1796424 273876 pts/0  Sl   22:43   0:02 /software/mysql/mysql3310/bin/mysqld --basedir=/software/mysql/mysql3310 --datadir=/software/mysql/mysql3310/data --plugin-dir=/software/mysql/mysql3310/lib/plugin --user=mysql --log-error=iz2zeaf5jdjve80rjlsjgnz.err --open-files-limit=4161 --pid-file=/software/mysql/mysql3310/mysqld.pid --port=3310
root      7434  0.0  0.0 113320  1624 pts/0    S    23:15   0:00 /bin/sh /software/mysql/mysql3307/bin/mysqld_safe --datadir=/software/mysql/mysql3307/data --pid-file=/software/mysql/mysql3307/mysqld.pid
mysql     8214  0.1 15.3 1777900 288340 pts/0  Sl   23:15   0:00 /software/mysql/mysql3307/bin/mysqld --basedir=/software/mysql/mysql3307 --datadir=/software/mysql/mysql3307/data --plugin-dir=/software/mysql/mysql3307/lib/plugin --user=mysql --log-error=iz2zeaf5jdjve80rjlsjgnz.err --open-files-limit=4161 --pid-file=/software/mysql/mysql3307/mysqld.pid --port=3307
root     27817  0.0  0.0 113324  1300 pts/3    S    22:06   0:00 /bin/sh /software/mysql/mysql3308/bin/mysqld_safe --datadir=/software/mysql/mysql3308/data --pid-file=/software/mysql/mysql3308/mysqld.pid
mysql    28625  0.0  3.6 1795656 69588 pts/3   Sl   22:06   0:03 /software/mysql/mysql3308/bin/mysqld --basedir=/software/mysql/mysql3308 --datadir=/software/mysql/mysql3308/data --plugin-dir=/software/mysql/mysql3308/lib/plugin --user=mysql --log-error=iz2zeaf5jdjve80rjlsjgnz.err --open-files-limit=4161 --pid-file=/software/mysql/mysql3308/mysqld.pid --port=3308
root     29106  0.0  0.0 113324  1640 pts/3    S    22:07   0:00 /bin/sh /software/mysql/mysql3309/bin/mysqld_safe --datadir=/software/mysql/mysql3309/data --pid-file=/software/mysql/mysql3309/mysqld.pid
mysql    29916  0.0 15.1 1795660 284968 pts/3  Sl   22:07   0:03 /software/mysql/mysql3309/bin/mysqld --basedir=/software/mysql/mysql3309 --datadir=/software/mysql/mysql3309/data --plugin-dir=/software/mysql/mysql3309/lib/plugin --user=mysql --log-error=iz2zeaf5jdjve80rjlsjgnz.err --open-files-limit=4161 --pid-file=/software/mysql/mysql3309/mysqld.pid --port=3309

2、mycat安装

使用wget命令下载Mycat-server-1.6.7.4-test-20191113141017-linux.tar.gz

mkdir /software/mycat
cd /software/mycat
wget http://dl.mycat.io/1.6.7.4/Mycat-server-1.6.7.4-test-20191113141017-linux.tar.gz

使用tar解压

tar zxvf Mycat-server-1.6.7.4-test-20191113141017-linux.tar.gz

3、mycat配置

mycat配置文件主要有schema.xml和server.xml。

server.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- - - Licensed under the Apache License, Version 2.0 (the "License");
    - you may not use this file except in compliance with the License. - You
    may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0
    - - Unless required by applicable law or agreed to in writing, software -
    distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT
    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the
    License for the specific language governing permissions and - limitations
    under the License. -->
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
    <system>
    <property name="nonePasswordLogin">0</property> <!-- 0为需要密码登陆、1为不需要密码登陆 ,默认为0,设置为1则需要指定默认账户-->
    <property name="useHandshakeV10">1</property>
    <property name="useSqlStat">0</property>  <!-- 1为开启实时统计、0为关闭 -->
    <property name="useGlobleTableCheck">0</property>  <!-- 1为开启全加班一致性检测、0为关闭 -->
        <property name="sqlExecuteTimeout">300</property>  <!-- SQL 执行超时 单位:秒-->
        <property name="sequnceHandlerType">5</property>
        <!--<property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property>-->
        <!--必须带有MYCATSEQ_或者 mycatseq_进入序列匹配流程 注意MYCATSEQ_有空格的情况-->
        <property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property>
    <property name="subqueryRelationshipCheck">false</property> <!-- 子查询中存在关联查询的情况下,检查关联字段中是否有分片字段 .默认 false -->
    <property name="sequenceHanlderClass">io.mycat.route.sequence.handler.HttpIncrSequenceHandler</property>
      <!--  <property name="useCompression">1</property>--> <!--1为开启mysql压缩协议-->
        <!--  <property name="fakeMySQLVersion">5.6.20</property>--> <!--设置模拟的MySQL版本号-->
    <!-- <property name="processorBufferChunk">40960</property> -->
    <!--
    <property name="processors">1</property>
    <property name="processorExecutor">32</property>
     -->
        <!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena | type 2 NettyBufferPool -->
        <property name="processorBufferPoolType">0</property>
        <!--默认是65535 64K 用于sql解析时最大文本长度 -->
        <!--<property name="maxStringLiteralLength">65535</property>-->
        <!--<property name="sequnceHandlerType">0</property>-->
        <!--<property name="backSocketNoDelay">1</property>-->
        <!--<property name="frontSocketNoDelay">1</property>-->
        <!--<property name="processorExecutor">16</property>-->
        <!--
            <property name="serverPort">8066</property> <property name="managerPort">9066</property>
            <property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property>
            <property name="dataNodeIdleCheckPeriod">300000</property> 5 * 60 * 1000L; //连接空闲检查
            <property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->
        <!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2为不过滤分布式事务,但是记录分布式事务日志-->
        <property name="handleDistributedTransactions">0</property>

            <!--
            off heap for merge/order/group/limit      1开启   0关闭
        -->
        <property name="useOffHeapForMerge">0</property>

        <!--
            单位为m
        -->
        <property name="memoryPageSize">64k</property>

        <!--
            单位为k
        -->
        <property name="spillsFileBufferSize">1k</property>

        <property name="useStreamOutput">0</property>

        <!--
            单位为m
        -->
        <property name="systemReserveMemorySize">384m</property>

        <!--是否采用zookeeper协调切换  -->
        <property name="useZKSwitch">false</property>

        <!-- XA Recovery Log日志路径 -->
        <!--<property name="XARecoveryLogBaseDir">./</property>-->

        <!-- XA Recovery Log日志名称 -->
        <!--<property name="XARecoveryLogBaseName">tmlog</property>-->
        <!--如果为 true的话 严格遵守隔离级别,不会在仅仅只有select语句的时候在事务中切换连接-->
        <property name="strictTxIsolation">false</property>

        <property name="useZKSwitch">true</property>

    </system>

    <!-- 全局SQL防火墙设置 -->
    <!--白名单可以使用通配符%或着*-->
    <!--例如<host host="127.0.0.*" user="root"/>-->
    <!--例如<host host="127.0.*" user="root"/>-->
    <!--例如<host host="127.*" user="root"/>-->
    <!--例如<host host="1*7.*" user="root"/>-->
    <!--这些配置情况下对于127.0.0.1都能以root账户登录-->
    <!--
    <firewall>
       <whitehost>
          <host host="1*7.0.0.*" user="root"/>
       </whitehost>
       <blacklist check="false">
       </blacklist>
    </firewall>
    -->

    <!--使用navicat等软件登录mycat的账户和密码-->
    <user name="mycat" defaultAccount="true">
        <property name="password">mycat</property>
    <!--类似于mysql的数据库名-->
        <property name="schemas">DB1</property>
        <property name="defaultSchema">DB1</property>
        <!--No MyCAT Database selected 错误前会尝试使用该schema作为schema,不设置则为null,报错 -->

        <!-- 表级 DML 权限设置 -->
        <!--
        <privileges check="false">
            <schema name="TESTDB" dml="0110" >
                <table name="tb01" dml="0000"></table>
                <table name="tb02" dml="1111"></table>
            </schema>
        </privileges>
         -->
    </user>

    <user name="user">
        <property name="password">user</property>
        <property name="schemas">DB1</property>
        <property name="readOnly">true</property>
        <property name="defaultSchema">DB1</property>
    </user>

</mycat:server>

schema.xml

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

    <schema name="DB1" checkSQLschema="false" sqlMaxLimit="100">
        <table name="ADMIN" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
    </schema>
    <!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"
        /> -->
    <dataNode name="dn1" dataHost="localhost1" database="DB1" />
    <dataNode name="dn2" dataHost="localhost1" database="DB2" />
    <dataNode name="dn3" dataHost="localhost1" database="DB3" />

    <!--<dataNode name="dn4" dataHost="sequoiadb1" database="SAMPLE" />
     <dataNode name="jdbc_dn1" dataHost="jdbchost" database="db1" />
    <dataNode   name="jdbc_dn2" dataHost="jdbchost" database="db2" />
    <dataNode name="jdbc_dn3"   dataHost="jdbchost" database="db3" /> -->
    <dataHost name="localhost1" maxCon="1000" minCon="10" balance="3"
              writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
        <heartbeat>show slave status</heartbeat>
        <!-- can have multi write hosts -->
        <writeHost host="hostM1" url="localhost:3307" user="root"
                   password="root">
            <readHost host="hosts1" url="localhost:3308" user="root"
                       password="root">
            </readHost>
        </writeHost>
        <writeHost host="hostM2" url="localhost:3310" user="root"
                    password="root">
            <readHost host="hosts2" url="localhost:3309" user="root"
                       password="root">
            </readHost>
        </writeHost>
    </dataHost>

    <!--
        <dataHost name="sequoiadb1" maxCon="1000" minCon="1" balance="0" dbType="sequoiadb" dbDriver="jdbc">
        <heartbeat>         </heartbeat>
         <writeHost host="hostM1" url="sequoiadb://1426587161.dbaas.sequoialab.net:11920/SAMPLE" user="jifeng"  password="jifeng"></writeHost>
         </dataHost>

      <dataHost name="oracle1" maxCon="1000" minCon="1" balance="0" writeType="0"   dbType="oracle" dbDriver="jdbc"> <heartbeat>select 1 from dual</heartbeat>
        <connectionInitSql>alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'</connectionInitSql>
        <writeHost host="hostM1" url="jdbc:oracle:thin:@127.0.0.1:1521:nange" user="base"   password="123456" > </writeHost> </dataHost>

        <dataHost name="jdbchost" maxCon="1000"     minCon="1" balance="0" writeType="0" dbType="mongodb" dbDriver="jdbc">
        <heartbeat>select   user()</heartbeat>
        <writeHost host="hostM" url="mongodb://192.168.0.99/test" user="admin" password="123456" ></writeHost> </dataHost>

        <dataHost name="sparksql" maxCon="1000" minCon="1" balance="0" dbType="spark" dbDriver="jdbc">
        <heartbeat> </heartbeat>
         <writeHost host="hostM1" url="jdbc:hive2://feng01:10000" user="jifeng"     password="jifeng"></writeHost> </dataHost> -->

    <!-- <dataHost name="jdbchost" maxCon="1000" minCon="10" balance="0" dbType="mysql"
        dbDriver="jdbc"> <heartbeat>select user()</heartbeat> <writeHost host="hostM1"
        url="jdbc:mysql://localhost:3306" user="root" password="123456"> </writeHost>
        </dataHost> -->
</mycat:schema>

首先schema标签中,设置的名字和server.xml设置的必须一致,它的子节点table表示设置哪个表,dataNode设置的是下面dataNode标签的name,表示将这个表分片到哪些database,dataHost属性设置的是下面dataHost的name。dataHost中writeHost设置的是3307(主服务器),readHost设置的是3308(从服务器)。第二个writeHost设置的和上面的相同,将writeHost设置的是3310(主服务器),readHost设置的是3308(从服务器)。

其中dataHost的balance、writeType、switchType属性。

balance, switchType, writeType
balance="0", 不开启读写分离机制,所有读操作都发送到当前可用的writeHost上。
balance="1",全部的readHost与stand by writeHost参与select语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且M1与M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡。
balance="2",所有读操作都随机的在writeHost、readhost上分发。
balance="3",所有读请求随机的分发到writeHost下的readhost执行,writeHost不负担读压力
writeType表示写模式
writeType="0",所有的操作发送到配置的第一个writehost
writeType="1",随机发送到配置的所有writehost
writeType="2",不执行写操作
switchType指的是切换的模式,目前的取值也有4种:
switchType=‘-1‘ 表示不自动切换
switchType=‘1‘ 默认值,表示自动切换
switchType=‘2‘ 基于MySQL主从同步的状态决定是否切换,心跳语句为show slave status,dnindex.properties里面的localhost1的值会发生改变。
switchType=‘3‘基于MySQL galary cluster的切换机制(适合集群)(1.4.1),心跳语句为show status like ‘wsrep%‘。

由于switchType设置的2,所以heartbeat的值是show slave status。

4、mycat运行

cd /software/mycat/mycat/bin
./mycat start

使用navicat连接(默认是8066端口)

Mycat搭建负载均衡,读写分离的Mysql集群

5、测试心跳

[root@iz2zeaf5jdjve80rjlsjgnz bin]# ./mysql -h127.0.0.1 -umycat -P9066 -pmycat
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 98
Server version: 5.7.24-log MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

使用show命令

mysql> show @@heartbeat;
+--------+-------+-----------+------+---------+-------+--------+---------+--------------+---------------------+-------+
| NAME   | TYPE  | HOST      | PORT | RS_CODE | RETRY | STATUS | TIMEOUT | EXECUTE_TIME | LAST_ACTIVE_TIME    | STOP  |
+--------+-------+-----------+------+---------+-------+--------+---------+--------------+---------------------+-------+
| hostM1 | mysql | localhost | 3307 |       1 |     0 | idle   |   30000 | 65,22,22     | 2019-11-24 23:57:12 | false |
| hostM2 | mysql | localhost | 3310 |       1 |     0 | idle   |   30000 | 99,33,33     | 2019-11-24 23:57:12 | false |
| hosts1 | mysql | localhost | 3308 |       1 |     0 | idle   |   30000 | 81,33,33     | 2019-11-24 23:57:12 | false |
| hosts2 | mysql | localhost | 3309 |       1 |     0 | idle   |   30000 | 82,30,30     | 2019-11-24 23:57:12 | false |
+--------+-------+-----------+------+---------+-------+--------+---------+--------------+---------------------+-------+
4 rows in set (0.00 sec)

mysql> 

rs_code:表示心跳状态

  • 0:INIT_STATUS 表示初始化状态
  • 1:OK_STATUS 表示正常状态
  • -1:ERROR_STATUS 表示连接出错
  • -2:TIMEOUT_STATUS 表示连接超时

若节点发生故障,会连续进行默认5个周期的检测,心跳连续失败后会变成 -1 。


其中RS_CODE都是1,表示所有mysql实例状态正常。

6、测试分片

mysql> create table admin (id bigint not null primary key,user_id varchar(100),traveldate DATE, fee decimal,days int);
Query OK, 0 rows affected (0.22 sec)
mysql> insert into ADMIN(id,user_id,traveldate,fee,days)  values(1,@@hostname,20160101,100,10);
Query OK, 1 row affected (0.28 sec)
mysql> insert into ADMIN(id,user_id,traveldate,fee,days)  values(5000001,@@hostname,20160101,100,10);
Query OK, 1 row affected (0.18 sec)
mysql> insert into ADMIN(id,user_id,traveldate,fee,days)  values(10000001,@@hostname,20160101,100,10);
Query OK, 1 row affected (0.19 sec)

因为在schema.xml中配置了table的rule属性为auto-sharding-long。

这个属性值在rule.xml的内容为

<tableRule name="auto-sharding-long">
        <rule>
            <columns>id</columns>
            <algorithm>rang-long</algorithm>
        </rule>
    </tableRule>
<function name="rang-long"
              class="io.mycat.route.function.AutoPartitionByLong">
        <property name="mapFile">autopartition-long.txt</property>
    </function>

对应的文件是autopartition-long.txt,打开这个文件

# range start-end ,data node index
# K=1000,M=10000.
0-500M=0
500M-1000M=1
1000M-1500M=2

表示id在0-500M范围内,500M-1000M范围内,1000M-1500M范围内,分别在不同的数据库。

Mycat搭建负载均衡,读写分离的Mysql集群

7、测试读写分离

查询ADMIN表。

mysql> select * from ADMIN;
+----------+-------------------------+------------+-----+------+
| ID       | USER_ID                 | TRAVELDATE | FEE | DAYS |
+----------+-------------------------+------------+-----+------+
|        1 | iz2zeaf5jdjve80rjlsjgnz | 2016-01-01 | 100 |   10 |
|  5000001 | iz2zeaf5jdjve80rjlsjgnz | 2016-01-01 | 100 |   10 |
| 10000001 | iz2zeaf5jdjve80rjlsjgnz | 2016-01-01 | 100 |   10 |
+----------+-------------------------+------------+-----+------+
3 rows in set (0.48 sec)

关闭3310主服务器。

kill -9 6218 7008

此时心跳测试出现-1。

dnindex.properties的localhost1发生改变,说明已经切换到另一个主服务器(关闭之前是0)。

#update
#Mon Nov 25 23:58:11 CST 2019
localhost1=1
mysql> show @@heartbeat;
+--------+-------+-----------+------+---------+-------+--------+---------+--------------+---------------------+-------+
| NAME   | TYPE  | HOST      | PORT | RS_CODE | RETRY | STATUS | TIMEOUT | EXECUTE_TIME | LAST_ACTIVE_TIME    | STOP  |
+--------+-------+-----------+------+---------+-------+--------+---------+--------------+---------------------+-------+
| hostM1 | mysql | localhost | 3307 |       1 |     0 | idle   |   30000 | 2,2,3        | 2019-11-25 00:23:16 | false |
| hostM2 | mysql | localhost | 3310 |      -1 |     2 | idle   |   30000 | 17,3,3       | 2019-11-25 00:23:16 | false |
| hosts1 | mysql | localhost | 3308 |       1 |     0 | idle   |   30000 | 2,2,2        | 2019-11-25 00:23:16 | false |
| hosts2 | mysql | localhost | 3309 |       1 |     0 | idle   |   30000 | 2,3,3        | 2019-11-25 00:23:16 | false |
+--------+-------+-----------+------+---------+-------+--------+---------+--------------+---------------------+-------+
4 rows in set (0.16 sec)

再次查询ADMIN表。

mysql> select * from ADMIN;
1105 - Table 'DB1.ADMIN' doesn't exist

结论:

1、设置了两个及以上的writeHost后,如果默认的writeHost出现宕机,那么会切换到第二个writeHost,相应的,dnindex.properties的localhost1值会改变,show @@heartbeat;命令查询到的HOST对应的RS_CODE会变成-1。

2、自动切换writeHost之后,第一个writeHost的值不会同步过去,所以再次查询ADMIN表会出现Table 'DB1.ADMIN' doesn't exist。

解决办法:

有四个mysql实例

mysql3307
mysql3308
mysql3309
mysql3310

在schema.xml中配置:

<writeHost Host="mysql3307">
    <readHost Host="mysql3308"></readHost>
</writeHost>
<writeHost Host="mysql3308">
    <readHost Host="mysql3309"></readHost>
</writeHost>
<writeHost Host="mysql3309">
    <readHost Host="mysql3310"></readHost>
</writeHost>

在3307宕机后,3308复制的3307,所以3308的数据和3307是一样的,切换到3308后,3309和3308的数据是同步的,以此类推,可以解决主宕机后数据不同步的问题。

上一篇:MySQL Route负载均衡与读写分离Docker环境使用


下一篇:《C++ Primer》 ---- 关于变量 与 基本类型