当前位置:   article > 正文

MyCat搭建MySQL双主双从及分库分表_mysql主从怎么配置mycat

mysql主从怎么配置mycat

一、MyCat的高级特性

1.MyCat读写分离概述

  • 读写分离,基本原理是让主数据库处理事务性增、删、改操作,而从数据库处理查询操作。
  • 从集中到分布,最基本的一个需求不是数据存储的瓶颈,而是在于计算的瓶颈,即SQL查询的瓶颈,我们知道,正常情况下,insert SQL就是几十个毫秒的时间写入完成,而系统中的大多数SelectSQL则要几秒到几分钟才能有结果,很多复杂的SQL,其消耗CPU的能力超强,不亚于死循环的威力。
    所以使用读写分离就是为了增强数据库的性能。

读写分离方案

  • MyCat的读写分离是建立在MySQL主从复制的基础上实现的,所以必须先搭建MySQL的主从复制。数据库读写分离对于大型系统或访问量很高的互联网应用来说,是必不可少的一个重要功能。
    MyCat实现的读写分离和自动切换机制,需要MySQL的主从复制技术配合。
  • MySQL主从复制的常见拓扑结构
    • 一主一从
      最基础的复制结构,用来分担之前单机数据库服务器的压力,可以进行读写分离。
    • 一主多从
      一台Slave承受不住读请求压力时,可以添加多台,进行负载均衡,分散读压力
    • 双主复制
      双主结构就是用来解决这个问题的,互相将对方作为自己的Master,自己作为对方的Slave来进行复制,但对外来说,还是一主一从。
    • 级联复制
      级联结构就是通过减少直接从属于Master的Slave数量,减轻Master的压力,分散复制请求,从而提高整体的复制效率。
    • 双主级联
      MySQL的复制结构有很多种方式,复制的最大问题是数据延时,选择复制结构时需要根据自己的具体情况,并评估好目标结构的延时对系统的影响。

2.搭建读写分离

  • 双击master会话,打开一个新窗口进行MyCat的操作。
  • 修改schema.xml,修改如下
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
        <!-- 数据库配置 与server.xml中的数据库对应 即逻辑库  -->
        <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="db_node">
        </schema>
        <!-- 分片配置 即分片节点 -->
        <dataNode name="db_node" dataHost="db_host" database="db_test" />
        <!-- 物理数据库配置 即节点主机 -->
        <dataHost name="db_host" maxCon="1000" minCon="10" balance="1"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <!-- can have multi write hosts -->
                <writeHost host="hostM1" url="192.168.126.17:3305" user="root"
                                   password="123456">
                        <readHost host="hostS1" url="192.168.126.18:3304" user="root" password="123456" />
                </writeHost>
</mycat:schema>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

参数:

  • schema参数:

    • schema:逻辑库
    • name:逻辑库名称
    • sqlMaxLimit:一次最多取多少条数据
    • table:逻辑表
    • dataNode:数据节点 对应dataNode标签
    • rule:分片规则 对应rule.xml
    • primaryKey:分片主键 可缓存
  • dataNode参数:

    • name:分片名字
    • dataHost:分片主机
    • database:分片数据库
  • dataHost参数:

    • dataHost:数据主机(节点主机)

    • dbType:数据库驱动native:MySQL JDBC: oracle SQLServer

    • Balance参数设置:

      • balance=“0”, 所有读操作都发送到当前可⽤的writeHost上。
      • balance=“1”,所有读操作都随机的发送到readHost。
      • balance=“2”,所有读操作都随机的在writeHost、readhost上分发
    • WriteType参数设置:

      • writeType=“0”, 所有写操作都发送到可⽤的writeHost上。
      • writeType=“1”,所有写操作都随机的发送到readHost。
      • writeType=“2”,所有写操作都随机的在writeHost、readhost分上发。
    • switchType参数设置:

      • switchType=“1”, 主从自动切换
      • switchType=“2”,从机延时超过slaveThreshold值时切换为主读
  • 在此新窗口mycat,重启mycat:mycat restart

  • 进入mycat数据库管理命令行:mysql -u root -p123456 -h 192.168.126.17 -P 8066
    这-p和密码不能有空格
    如果连接不上,可能是配置文件schema.xml配错了,或者mysql没启动等原因

  • 在数据端口8066命令行使用TESTDB数据库:use TESTDB

  • 从MyCat数据端口命令行插入一条数据:insert into dog values(2,@@hostname);
    @@hostname是docker的容器id,因为是插入操作,所以是master的id。

3.搭建MySQL双主双从

1.配置MySQL的配置文件

  • 双主双从,即将对方作为自己的master,自己作为对方的slave来进行复制,但对外来讲,还是一主一从。
    一主一从,读写压力比较大会出现性能性问题;使用一主多从来解决,防止master单点故障;又使用双主双从来解决。

  • 准备4台虚拟机或者在,搭建双主双从
    不小心区分了端口号,但是没有影响,问题不大

  • 第一台虚拟机的MySQL服务master1docker run -d -p 3360:3306 -e MYSQL_ROOT_PASSWORD=123456 --name=master1 mysql:latest

  • 第二台虚拟机的MySQL服务slave1docker run -d -p 3370:3306 -e MYSQL_ROOT_PASSWORD=123456 --name=slave1 mysql:latest

  • 第三台虚拟机的MySQL服务master2docker run -d -p 3380:3306 -e MYSQL_ROOT_PASSWORD=123456 --name=master2 mysql:latest

  • 第四台虚拟机的MySQL服务slave2docker run -d -p 3390:3306 -e MYSQL_ROOT_PASSWORD=123456 --name=slave2 mysql:latest

修改master1配置文件

  • 将mysql.cnf文件拷贝出来:docker cp master1:/etc/my.cnf .

  • 修改配置文件my.cnf:vim my.cnf,添加如下配置:

    [mysqld]
    
    # 配置唯一ID
    server-id=17
    # 开启二进制日志
    log-bin=mysql-bin
    # 设置不要复制的数据库(可设置多个)
    binlog-ignore-db=mysql
    binlog-ignore-db=information_schema
    # 设置logbin格式
    binlog_format=STATEMENT
    # 在作为从数据库的时候,有写入操作也要更新二进制文件
    log-slave-updates
    # 指自增字段的起始值,其默认值是1,取值范围是1~65535
    auto_increment_increment=2
    # 指字段一次递增多少,取值范围是1~65535
    auto_increment_offset=1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 将my.cnf文件拷贝回master1容器:docker cp my.cnf master1:/etc/

修改master2配置文件

  • 将mysql.cnf文件拷贝出来:docker cp master2:/etc/my.cnf .

  • 修改配置文件my.cnf:vim my.cnf,添加如下配置:

    [mysqld]
    
    # 配置唯一ID
    server-id=19
    # 开启二进制日志
    log-bin=mysql-bin
    # 设置不要复制的数据库(可设置多个)
    binlog-ignore-db=mysql
    binlog-ignore-db=information_schema
    # 设置需要复制的数据库
    #binlog-do-db=需要复制的master数据库
    # 设置logbin格式
    binlog_format=STATEMENT
    # 在作为从数据库的时候,有写入操作也要更新二进制文件
    log-slave-updates
    # 指自增字段的起始值,其默认值是1,取值范围是1~65535
    auto_increment_increment=2
    # 指字段一次递增多少,取值范围是1~65535
    auto_increment_offset=1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 将my.cnf文件拷贝回master2容器:docker cp my.cnf master2:/etc/

修改slave1配置文件

  • 将mysql.cnf文件拷贝出来:docker cp slave1:/etc/my.cnf .

  • 修改配置文件my.cnf:vim my.cnf,添加如下配置:

    [mysqld]
    
    # 配置唯一ID
    server-id=18
    # 开启中继日志
    relay-log=mysql-relay
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 将my.cnf文件拷贝回slave1容器:docker cp my.cnf slave1:/etc/

修改slave2配置文件

  • 将mysql.cnf文件拷贝出来:docker cp slave2:/etc/my.cnf .

  • 修改配置文件my.cnf:vim my.cnf,添加如下配置:

    [mysqld]
    
    # 配置唯一ID
    server-id=20
    # 开启中继日志
    relay-log=mysql-relay
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 将my.cnf文件拷贝回slave2容器:docker cp my.cnf slave2:/etc/

  • 重启所有MySQL服务:

    • docker restart master1
    • docker restart master2
    • docker restart slave1
    • docker restart slave2
  • 进入容器:docker exec -it 容器名或id bash
    进入docker容器后只显示bash-4.4,则输入:cp /etc/skel/.bash* /root/
    exit退出容器,再进容器就可以了。
    如果此时若容器没有启动,可能是配置文件有问题。

  • 在两个master中,进入到Mysql命令行,创建数据同步用户slave:
    CREATE USER slave IDENTIFIED BY '123456';
    GRANT REPLICATION SLAVE ON *.* TO 'slave'@'%';
    GRANT ALL PRIVILEGES ON *.* TO 'slave'@'%';
    FLUSH PRIVILEGES;

  • 因为版本是大于8.0.4的,需要在两个master上更换密码机制并修改密码:ALTER USER 'slave'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
    查看用户的密码机制:select user,host,plugin from user;

  • 查看master1的状态:show master status;

  • 从库slave1连接主库master1,在从库slave1中的mysql命令行执行:change master to master_host='192.168.126.17',master_user='slave',master_port=3360,master_password='123456',master_log_file='mysql-bin.000007',master_log_pos=157;

  • 启动复制功能:start slave;

  • 查看master2的状态:show master status;

  • 从库slave2连接主库master2,在从库slave2中的mysql命令行执行:change master to master_host='192.168.126.19',master_user='slave',master_port=3380,master_password='123456',master_log_file='mysql-bin.000006',master_log_pos=157;

  • 启动复制功能:start slave;

  • 查看连接状态:show slave status \G;
    当两个线程都显示yes时,配置主从复制成功。

  • 配置master1成为master2的slave
    在master1的mysql命令行,配置为master2的slave:change master to master_host='192.168.126.19',master_user='slave',master_port=3380,master_password='123456',master_log_file='mysql-bin.000007',master_log_pos=157;

  • 配置master2成为master1的slave
    在master2的mysql命令行,配置为master1的slave:change master to master_host='192.168.126.17',master_user='slave',master_port=3360,master_password='123456',master_log_file='mysql-bin.000008',master_log_pos=157;

  • 启动复制功能:start slave;

  • 查看连接状态:show slave status \G;
    当显示Slave_IO_Running: Yes,Slave_SQL_Running: Yes时,配置成功。

2.配置MyCat的配置文件

  • 进入到/usr/local/mycat/conf目录下,编辑schema.xml文件:vim schema.xml,配置如下:

    <?xml version="1.0"?>
    <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
    <mycat:schema xmlns:mycat="http://io.mycat/">
    
            <!-- 数据库配置 与server.xml中的数据库对应 即逻辑库  -->
            <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="db_node">
            </schema>
    
            <!-- 分片配置 即分片节点 -->
            <dataNode name="db_node" dataHost="db_host" database="db_test" />
    
            <!-- 物理数据库配置 即节点主机 -->
            <dataHost name="db_host" maxCon="1000" minCon="10" balance="1"
                              writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                    <heartbeat>select user()</heartbeat>
                    <!-- 读写分离 双主双从 -->
                     <writeHost host="hostM1" url="192.168.126.17:3360" user="root" password="123456">
                            <readHost host="hostS1" url="192.168.126.18:3370" user="root" password="123456" />
                    </writeHost>
                     <writeHost host="hostM2" url="192.168.126.19:3380" user="root" password="123456">
                            <readHost host="hostS2" url="192.168.126.20:3390" user="root" password="123456" />
                    </writeHost>
    
            </dataHost>
    </mycat:schema>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    即在一主一从的配置基础上,再配置另一个主从。

    • 参数:
      • writeType=“0”:所有写操作发送到配置的第一个 writeHost,第一个挂了切到还生存的第二个writeHost,重新启动后已切换后的为准,切换记录在配置文件中:dnindex.properties.
        即第二个writeHost为第一个的备机。
      • writeType=“1”:所有写操作都随机的发送到配置的 writeHost,1.5 以后废弃不推荐。
      • switchType=“-1” :表示不自动切换 mysql 实例
      • switchType=“1” :默认值,自动切换
  • 启动mycat:mycat start

二、MyCat分片技术

1.垂直拆分-分库

  • 目前很多互联网系统都存在单表数据量过大的问题,这就降低了查询数据,影响了客户体验。为了提高查询效率,可以优化SQL语句,优化表结构和索引,不过对那些百万级千万级的数据库表,即便是优化过后,查询速度还是满足不了要求。

  • 垂直分割是指数据表列的拆分,把一张列比较多的表拆分为多张表。表的记录并不多,但是字段却很长,表占用空间很大,检索表的时候需要执行大量的IO,严重降低了性能。这时需要把大的字段拆分到另一个表,并且该表与原表是一对一的关系。
    拆分原则:

    • 把不常用的字段单独放在一张表
    • 把text,blob等大字段拆分出来放在附表中
    • 经常组合查询的列放在一张表中
  • 修改mycat目录下的schema.xml配置文件,配置如下:

    <?xml version="1.0"?>
    <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
    <mycat:schema xmlns:mycat="http://io.mycat/">
    
            <!-- 数据库配置 与server.xml中的数据库对应 即逻辑库  -->
            <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
            		<!-- 垂直拆分 -->
                    <table name="customer" dataNode="dn2"/>
            </schema>
    
            <!-- 分片配置 即分片节点 实现垂直分库 -->
            <dataNode name="dn1" dataHost="host1" database="orders" />
            <dataNode name="dn2" dataHost="host2" database="orders" />
            <!-- 物理数据库配置 即节点主机 -->
            <dataHost name="host1" maxCon="1000" minCon="10" balance="1"
                              writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                    <heartbeat>select user()</heartbeat>
                     <writeHost host="hostM1" url="192.168.126.17:3360" user="root" password="123456">
                     </writeHost>
            </dataHost>
    
            <dataHost name="host2" maxCon="1000" minCon="10" balance="1"
                              writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                    <heartbeat>select user()</heartbeat>
                     <writeHost host="hostM1" url="192.168.126.19:3380" user="root" password="123456">
                     </writeHost>
            </dataHost>
    </mycat:schema>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
  • 停止之前两个master的相互复制,都在mysql命令行中执行:stop slave;

  • 两个master同时创建数据库orders:create database orders;

  • 将两个master的密码机制都改为mysql_native_password:ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
    这样MyCat才能访问到,否则密码不正确,因为Mysql是最新版的

  • 重启mycat:mycat restart

  • 进入到master1的Mysql命令行:mysql -u root -p123456 -h 192.168.126.17 -P 8066

  • 在MyCat中,使用数据库TESTDB:use TESTDB;

  • 创建表customer:create table customer(id int auto_increment,name varchar(200),primary key(id));

  • 创建表orders:create table orders(id int auto_increment,order_type int,customer_id int,amount decimal(10,2),primary key(id));

  • 创建表orders_detail:create table orders_detail(id int auto_increment,order_id int,detail varchar(200),primary key(id));

  • 创建表dict_orders_type:create table dict_orders_type(id int auto_increment,order_type varchar(200),primary key(id));

  • 此时在mycat的TESEDB逻辑库中,创建4个表,但是customer表被创建到master2的orders,其他都在master1的orders中,实现了分库。
    即指定customer表都在master2的orders中执行,其余都在master1的orders中执行。

2.水平拆分-分表

  • 垂直拆分只是把表按模块分到不同数据库,但没有解决单表大数据量的问题。
  • 水平拆分,相对于垂直拆分水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分数据。
    数据的水平切分是按照数据行的切分,就是将表中的某些行切分到一个数据库,而将另外某些行又切分到其他数据库中。

实现分表:

  1. 选择要拆分的表
    MySQL单表存储条数是有瓶颈的,单表达到1000W条就达到了瓶颈,会影响查询效率,需要进行水平拆分(分表)来优化。

  2. 分表字段
    orders表为例,可以根据不同字段进行分表。
    字段id:查询订单注重时效,历史订单被查询的次数少,如此分片会造成一个节点访问多,一个节点访问少,不平均。
    字段customer_id:根据客户id去分,两个节点访问平均。

  3. 修改配置文件schema.xml

     <!-- 水平拆分 -->
     <table name="orders" dataNode="dn1,dn2" rule="mod-rule" />
    
    • 1
    • 2

    即为orders表设置数据节点为dn1、dn2,并指定分片规则为mod-rule(自定义)

  4. 修改配置文件rule.xml

    • 分片规则:在rule配置文件里新增分片规则mod-rule,并指定规则适用字段customer_id,还有选择分片算法mod_long(对字段求模运算),customer_id对两个节点求模,根据结果分片。配置算法mod-long参数count为2,两个节点
    • 代码:
    <tableRule name="mod-rule">
                <rule>
                        <columns>customer_id</columns>
                        <algorithm>mod-long</algorithm>
                </rule>
     </tableRule>
     <function name="mod-long" class="io.mycat.route.function.PartitionByMod">
                <!-- how many data nodes -->
                <property name="count">2</property>
     </function>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  5. 在master2中的orders数据库中创建orders表

    create table orders(
      id int auto_increment,
      order_type int,
      customer_id int,
      amount decimal(10,2),
      primary key(id)
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  6. 重启mycat,生效配置:mycat restart

  7. 在mycat的8066端口插入数据,测试是否实现水平拆分

    INSERT INTO orders(id,order_type,customer_id,amount) VALUES (1,101,100,100100);
    INSERT INTO orders(id,order_type,customer_id,amount) VALUES(2,101,100,100300);
    INSERT INTO orders(id,order_type,customer_id,amount) VALUES(3,101,101,120000);
    INSERT INTO orders(id,order_type,customer_id,amount) VALUES(4,101,101,103000);
    INSERT INTO orders(id,order_type,customer_id,amount) VALUES(5,102,101,100400);
    INSERT INTO orders(id,order_type,customer_id,amount) VALUES(6,102,100,100020);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

在master1和master通过select查看分片是否正确即可。

3.ER表

  • 问题,orders表分片了,那和它相关的orders_detail表未分片,join联查时,master1正常查询出结果,master2由于没有orders_detail表,则报错,最后聚合结果也是错的。

  • ER表,将子表的存储位置依赖于主表,并且物理上紧邻存放,因此彻底解决了JOIN的效率和性能问题,根据这一思路,提出了基于E-R关系的数据分片策略,子表的记录与所关联的父表记录存放在同一个数据分片上。

  • 修改配置文件schema.xml,代码如下:

     <table name="orders" dataNode="dn1,dn2" rule="mod-rule" >
            <!-- ER表 -->
            <childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
    </table>
    
    • 1
    • 2
    • 3
    • 4

即在原先的table下添加一个子表childTable。

  • 在master2中的orders数据库中创建orders_detail表:

    create table orders_detail(
    id int auto_increment,
    order_id int,
    detail varchar(200),
    primary key(id)
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 重启Mycat:mycat restart

  • 进入到8066端口,在mycat中插入orders_detail数据:

    insert into orders_detail(detail, order_id) values('detail1',1);
    insert into orders_detail(detail, order_id) values('detail1',2);
    insert into orders_detail(detail, order_id) values('detail1',3);
    insert into orders_detail(detail, order_id) values('detail1',4);
    insert into orders_detail(detail, order_id) values('detail1',5);
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 在Mycat中使用join来测试ER表配置是否成功:select a.*,b.detail from orders a join orders_detail b on a.id=b.order_id;

4.全局表

  • 全局表,在分片的情况下,当业务表因为规模而进行分片以后,业务表与这些附属的字典表之间的关联,就成了比较棘手的问题,字典表具有以下特性:
    • 变动不频繁
    • 数据量总体变化不大
    • 数据规模不大,很少有超过10W条记录
  • 全局表特征:
    • 全局表插入更新操作会实时在所有节点上执行,保持各个分片的数据一致。
    • 全局表的查询操作,只从一个节点获取。
    • 全局表可以和任何一个表进行JOIN操作。
  • 将字典表或者符合字典表特性的一些表定义为全局表,则从另外一个方面,很好的解决了数据JOIN的难题。通过全局表+基于E-R关系的分片策略,可以满足80%的企业应用开发。
  • 在master2的orders数据库中创建dict_order_type表:
create table dict_order_type(
  id int auto_increment,
  order_type varchar(200),
 primary key(id)
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 在master1修改一下表名: alter table dict_orders_type rename to dict_order_type;
    之前创建table错了好像,没有错就不用改名字

  • 在schema.xml配置全局表,如下

    <!-- 全局表 -->
    <table name="dict_order_type" dataNode="dn1,dn2" type="global"></table>
    
    
    • 1
    • 2
    • 3
  • 重启MyCat:mycat restart

  • 重新进入8066端口,插入数据:

    insert into dict_order_type(id,order_type) values(101,'type1');
    insert into dict_order_type(id,order_type) values(102,'type2');
    
    • 1
    • 2
  • 此时在master1和master2都查询得到插入的两条记录:select * from dict_order_type;

三、MyCat分片规则

1. 取模分片

  • 取模分片就是根据数据表的某一个字段,通常是某一个整数型的字段,对其进行十进制的取模运算,将运算结果作为MyCat的路由结果。
    • 优点:这种策略可以很好的分散数据库写的压力。
    • 缺点:出现了范围查询,就需要MyCat去合并结果,当数据量偏高的时候,这种夸库查询+合并结果消耗的时间可能会增加很多,尤其是还出现order by的时候。
  • tableRule标签,定义表规则
    • name:指定唯一名字,用于标识不同的表规则。内嵌的rule标签则指定对物理表中的哪一列进行拆分和使用什么路由算法。
    • columns:指定要拆分的列名字。
    • algorithm:使用function标签的name属性。连接表规则和具体路由算法。多个表规则(tableRule)可以连接到同一个路由算法(function)上。
  • function标签,定义具体路由算法
    • name:指定算法的名字
    • class:指定路由算法具体的类名字
    • property:具体算法需要用的一些属性
    • count:表示需要取模的最大值,将数据分成该配置的切片

2. 枚举分片

  • 实现原理,有些业务需要按照省份或区县来做保存,这类业务使用本条规则。

  • 实现过程,需要定义三个值,规则均是在rule.xml中定义。

    • tableRule
    • function
    • mapFile
  • 在master1和master2的orders数据库中,创建示例表orders_ware_info

    CREATE TABLE orders_ware_info(
     `id` INT AUTO_INCREMENT comment '编号',
     `order_id` INT comment '订单编号',
     `address` VARCHAR(200) comment '地址',
     `areacode` VARCHAR(20) comment '区域编号',
     PRIMARY KEY(id)
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 修改schema.xml文件

     <!-- 枚举分片 -->
     <table name="orders_ware_info" dataNode="dn1,dn2" rule="sharding-by-intfile" />
    
    • 1
    • 2

    即表orders_ware_info,按照规则sharding-by-intfile进行分片

  • 修改rule.xml文件

    <!-- 枚举分片 -->
    <tableRule name="sharding-by-intfile">
             <rule>
                     <columns>areacode</columns>
                     <algorithm>hash-int</algorithm>
             </rule>
    </tableRule>
    <function name="hash-int" class="io.mycat.route.function.PartitionByFileMap">
    	<property name="mapFile">partition-hash-int.txt</property>
    	<property name="type">1</property>
        <property name="defaultNode">0</property>
    </function>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    即按照字段areacode进行枚举分片。
    参数:

    • mapFile:指的是配置文件名。
    • type:默认值为0,0表示Integer,非零表示String。
    • defaultNode 默认节点:小于0表示不设置默认节点,大于等于0表示设置默认节点 默认节点的作用:枚举分片时,如果碰到不识别的枚举值,就让它路由到默认节点。
  • 修改partition-hash-int.txt文件

    10086=0
    10010=1
    
    • 1
    • 2

    0则代表第一个节点,1则代表第二个节点

  • 重启mycat:mycat restart

  • 进入8066端口,使用TESTDB数据库,插入2条数据:

INSERT INTO orders_ware_info(id, order_id,address,areacode) VALUES (1,1,'北京','10086');
INSERT INTO orders_ware_info(id, order_id,address,areacode) VALUES (2,2,'天津','10010');
  • 1
  • 2

3.范围分片

  • 实现原理,此分片适用于提前规划好分片字段的某个范围属于哪个分片。

    • 优点:适用于想明确知道某个分片字段的某个范围具体在哪一个节点。
    • 缺点:如果短时间内有大量的批量插入操作,那么某个分片节点可能一下会承受比较大的数据库压力,而别的分片节点此时可能处于闲置状态,无法利用其他节点进行分担压力(热点数据问题)。
  • 在master1和master2的orders数据库中,创建示例表payment_info

    CREATE TABLE payment_info
    (
     `id` INT AUTO_INCREMENT comment '编号',
     `order_id` INT comment '订单编号',
     `payment_status` INT comment '支付状态',
     PRIMARY KEY(id)
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 修改配置文件schema.xml

     <!-- 范围分片 -->
     <table name="payment_info" dataNode="dn1,dn2" rule="auto-sharding-long" />
    
    • 1
    • 2
  • 修改配置文件rule.xml

    <!-- 范围分片 -->
    <tableRule name="auto-sharding-long">
           <rule>
                   <columns>order_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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    参数

    • mapFile:指的是配置文件名
    • type:默认值为0,0表示Integer,非零表示String。因为我接下来的测试是基于省份分片,所以需type指定为1。
    • defaultNode 默认节点:小于0表示不设置默认节点,大于等于0表示设置默认节点 默认节点的作用:枚举分片时,如果碰到不识别的枚举值,就让它路由到默认节点。
  • 修改文件autopartition-long.txt

    0-102=0
    103-200=1
    
    • 1
    • 2
  • 重启mycat:mycat restart

  • 在8066端口插入数据用来测试:

    INSERT INTO payment_info (id,order_id,payment_status) VALUES (1,101,0);
    INSERT INTO payment_info (id,order_id,payment_status) VALUES (2,102,1);
    INSERT INTO payment_info (id,order_id ,payment_status) VALUES (3,103,0);
    INSERT INTO payment_info (id,order_id,payment_status) VALUES (4,104,1);
    
    • 1
    • 2
    • 3
    • 4

4.按日期(天)分片

  • 实现原理,此规则按天分片,设定时间格式、范围

  • 在master1和master2中创建示例表login_info

    create table login_info(
      id int auto_increment comment '编号',
      user_id int comment '用户编号',
      login_date date comment '登录时间',
     primary key(id)
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 修改配置文件schema.xml

    <!-- 日期(天)分片 -->
    <table name="login_info" dataNode="dn1,dn2" rule="sharding-by-date" />
    
    • 1
    • 2
  • 修改配置文件rule.xml

    <!-- 日期(天)分片 -->
    <tableRule name="sharding-by-date">
            <rule>
                    <columns>login_date</columns>
                    <algorithm>partbyday</algorithm>
            </rule>
    </tableRule>
    <!-- 日期(天)分片 -->
    <function name="partbyday" class="io.mycat.route.function.PartitionByDate">
            <property name="dateFormat">yyyy-MM-dd</property>
            <property name="sNaturalDay">0</property>
            <property name="sBeginDate">2022-01-01</property>
            <property name="sEndDate">2022-01-04</property>
            <property name="sPartionDay">2</property>
    </function>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    参数

    • columns:分片字段,algorithm:分片函数
    • dateFormat:日期格式
    • sBeginDate:开始日期
    • sEndDate:结束日期,则代表数据达到了这个日期的分片后循环从开始分片插入
    • sPartionDay:分区天数,即默认从开始日期算起,每隔 2 天一个分区
  • 重启mycat:mycat restart

  • 在8066端口插入数据用来测试:

    insert into login_info(id,user_id,login_date) values(1,101,'2022-01-01');
    insert into login_info(id,user_id,login_date) values(2,102,'2022-01-02');
    insert into login_info(id,user_id,login_date) values(3,103,'2022-01-03');
    insert into login_info(id,user_id,login_date) values(4,104,'2022-01-04');
    insert into login_info(id,user_id,login_date) values(5,105,'2022-01-05');
    insert into login_info(id,user_id,login_date) values(6,106,'2022-01-06');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

5.全局序列

  • 在实现分库分表的情况下,数据库自增主键已无法保证全局唯一。

解决方案

  • 本地文件方式
    此方式MyCat将sequence配置到文件中,当使用到sequence中的配置后,MyCat会更新classpath中的sequence_conf.properties文件中sequence当前的值。

    • 优点:本地加载,读取速度较快
    • 缺点:抗风险能力差,MyCat所在主机宕机后,无法读取本地文件。
  • 本地时间戳方式
    全局序列ID=64位二进制(42(毫秒)+ 5(机器ID)+ 5(业务编码)+ 12(重复累加))换算成十进制,为18位数的long类型,每毫秒可以并发12位二进制的累加

    • 优点:配置简单
    • 缺点:18位ID过长
  • 数据库方式
    利用数据库的一个表来进行计数累加。但是并不是每次生成序列都读写数据库,这样效率太低。MyCat会预加载一部分号段到MyCat内存中,这样大部分读写序列都是在内存中完成的。如果内存中的号段用完了,MyCat会再向数据库要一次。
    原理:在数据库中建立一张表,存放sequence名称(name),sequence当前值(current_value),步长(increment int类型每次读取多少个sequence,假设为K)等信息。

数据库解决全局序列

  • 修改MyCat配置文件server.xml

    <!-- 全局序列类型:0-本地文件,1-数据库方式,2-时间戳方式 -->
    <property name="sequenceHandlerType">1</property>
    
    • 1
    • 2
  • 修改配置文件schema.xml

    <!-- 全局序列-数据库方式 -->
    <table name="test" primaryKey="id" autoIncrement="true" dataNode="dn1,dn2" rule="mod-long" />
    <table name="mycat_sequence" primaryKey="name" dataNode="dn2" />
    
    • 1
    • 2
    • 3

    即前面一个表用来做测试,后面一个表就是存放sequence的表。

  • 修改文件sequence_db_conf.properties

    GLOBAL=dn1
    COMPANY=dn1
    CUSTOMER=dn1
    ORDERS=dn1
    MYCAT=dn2
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 在master2的orders数据库中添加MYCAT_SEQUENCE表

    DROP TABLE IF EXISTS MYCAT_SEQUENCE;
    CREATE TABLE MYCAT_SEQUENCE (name VARCHAR(50) NOT NULL,current_value INT NOT NULL,increment INT NOT NULL DEFAULT 100, PRIMARY KEY(name)) ENGINE=InnoDB;
    
    • 1
    • 2
  • MYCAT_SEQUENCE表插入初始记录

    INSERT INTO MYCAT_SEQUENCE(name,current_value,increment) VALUES ('mycat', -99, 100);
    
    • 1

    代表插入了一个名为mycat的sequence,当前值为-99,步长为100。

  • 在master2的orders数据库中创建全局序列所需存储过程

    • 获取当前sequence的值
      DELIMITER $$ 
      CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50)) RETURNS VARCHAR(64)
      DETERMINISTIC 
      BEGIN
      DECLARE retval VARCHAR(64);
      SET retval="-999999999,null";
      SELECT CONCAT(CAST(current_value AS CHAR),",",CAST(increment AS CHAR)) INTO retval FROM
      MYCAT_SEQUENCE WHERE NAME = seq_name;
      RETURN retval;
      END $$
      DELIMITER ;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    • 设置sequence的值
      DELIMITER $$
      CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50),VALUE INTEGER) RETURNS 
      VARCHAR(64)
      DETERMINISTIC
      BEGIN
      UPDATE MYCAT_SEQUENCE
      SET current_value = VALUE
      WHERE NAME = seq_name;
      RETURN mycat_seq_currval(seq_name);
      END $$
      DELIMITER ;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    • 获取下一个sequence的值
      DELIMITER $$
      CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50)) RETURNS VARCHAR(64) 
      DETERMINISTIC
      BEGIN
      UPDATE MYCAT_SEQUENCE
      SET current_value = current_value + increment WHERE NAME = seq_name;
      RETURN mycat_seq_currval(seq_name);
      END $$
      DELIMITER ;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
  • 重启mycat:mycat restart

  • 在master1和master2中创建test表:

    create table test(id int,name varchar(10));
    
    • 1
  • 在MyCat中向test表中添加测试数据

    insert into test(id,name) values(next value for MYCATSEQ_MYCAT,(select database()));
    
    • 1
  • 查询数据进行验证,看是否自增:SELECT * FROM test order by id asc;

总结:

  1. MyCat的读写分离及自动切换技术,是基于MySQL的主从复制技术实现的。
    MySQL主从复制解决数据一致性的问题。MyCat的读写分离主要是为了实现高性能。
    在schema.xml配置文件中配置逻辑库等,需要跟server.xml中的schema对应。
  2. MySQL双主双从,实现了读写分离、高可用、高性能,即两个readHost都可以对外提供读操作;而两个writeHost中,第一个writeHost对外提供写操作,第二个writeHost为第一个的备机。在一主多从到双主双从,可以减少单个writeHost的复制压力(即高性能),也实现了写操作的高可用。
    它们的结构则是两个主从,并且这两个主会相互成为对方的从。
  3. 垂直拆分,即将不常用字段和大字段进行拆分,将常用的组合查询的列放在一张表。
    垂直拆分用于分库,在配置文件schema.xml中配置,即schame参数有一个分片节点,当不指定表给某个节点时,全部的SQL都交由这个节点处理;有指定则将指定表的SQL交由指定节点处理。指定节点会有一个对应的节点主机dataHost。
    MyCat垂直拆分主要解决性能问题。
  4. 水平拆分,数据的水平切分是按照数据行的切分,就是将表中的某些行切分到一个数据库,而将另外某些行又切分到其他数据库中。
    需要在schema.xml配置需要水平切分的表,切分到哪个DataNode,以及分片规则rule的名字,对应到rule.xml中配置。
    配置相比于垂直切分,水平切分多配置了一个rule.xml;在主机层面,垂直切分,将表按字段切分到多个主机的同一个数据库名,水平切分,将表按行切分到多个主机的同一个数据库名。
    MyCat水平拆分主要解决表数据量大的问题。
  5. ER表,解决JOIN的效率和性能问题,即用来处理表与子表关联查询的问题,在schema.xml中配置,在表中添加一个子表childTable。
    即水平拆分时,行数据会被切分,在joid查询时,又需要数据库中存在对应的子表行数据,所以使用ER表来让子表的行数据跟随对应表的行数据。即joinKey与parentKey的值相同的会被分配在同一个主机的同一个数据库中。
  6. 全局表,即所有分片节点都有的一个表,并且插入更新操作在所有节点实时执行。
    主要解决变动不频繁和数据量总体不大的数据,通过type="global"来设置全局表。
  7. 分片规则有取模、枚举、范围、日期、全局序列等。
    分片枚举指的是需要按照省份或者区县来做保存的。
    范围分片缺点是处理热点数据问题。
    全局序列解决分表分库后id不唯一的问题。
    通过设置server.xml的sequenceHandlerType为1(即以数据库的方式)来实现全局序列。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/407214
推荐阅读
相关标签
  

闽ICP备14008679号