美文网首页一些收藏Mysqlmysql
MyCat 1.6.7(一)MySQL高可用及分库分表

MyCat 1.6.7(一)MySQL高可用及分库分表

作者: _大叔_ | 来源:发表于2022-05-30 19:27 被阅读0次

    简介

    MyCat 是目前最流行的基于 java 语言编写的数据库中间件,是一个实现了 MySQL 协议的服务器,前端用户可以把它看作是一个数据库代理,用 MySQL 客户端工具和命令行访问,而其后端可以用 MySQL 原生协议与多个 MySQL 服务器通信,也可以用 JDBC 协议与大多数主流数据库服务器通信,其核心功能是分库分表。配合数据库的主从模式还可实现读写分离。官方学习地址

    MyCat2 已经可以使用,目前仅支持java8
    垂直分库 就是微服务的形式,把一个服务单元拆到一个库中
    垂直分表 把一个大表,字段多的表,拆分成多个小表,或按照查询逻辑拆分主要信息表或次要信息表
    水平分库 把数据库集群,数据通过hash 或 取模 的方式散到你的数据库节点中
    水平分表 只针对大表进行切分,按一定的逻辑进行切分

    功能

    1.读写分离

    2.数据分片,垂直拆分(分库)、水平拆分(分表)、垂直+水平拆分(分库分表)

    3.多数据源整合

    原理

    MyCat 的原理种最重要的一个动词时”拦截“,它拦截了用户发送过来的SQL语句,首先对SQL语句做了一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此SQL发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户

    这种方式把数据库的分布式从代码中解耦出来,程序员察觉不出来后台使用MyCat还是MySQL

    安装及操作

    github 地址,这里要找的是打包过的文件下载,不要下载源码,除非你要看源码或改。

    # 下载
    wget http://dl.mycat.org.cn/1.6.7.6/20210730131311/Mycat-server-1.6.7.6-release-20210730131311-linux.tar.gz
    #解压
    tar -xvf Mycat-server-1.6.7.6-release-20210730131311-linux.tar.gz
    # 移动到指定文件夹
    mv mycat/ /opt/software/
    

    启动

    # 控制台启动,确认启动过程不会出错
    bin/mycat console
    # 后台启动
    bin/mycat start
    

    配置文件介绍:
    1.schema.xml 定义逻辑库,表、分片节点等内容
    2.rule.xml 定义分片规则
    3.server.xml 定义用户以及系统相关变量,如端口等

    MyCat连接MySQL换驱动成8.X

    1. 上传 mysql-connector-java-8.0.19.jar 到 mycat 的 lib 目录下
    2. 给文件赋权 chmod 777 mysql-connector-java-8.0.19.jar
    3. 重新启动。mycat 会自动检测驱动,所以不需要我们操心,如果遇到如下提示,则替换驱动成功
    Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
    

    登录MyCat

    修改 server.xml

    <!-- 连接mycat的用户信息 -->
    <user name="mycat" defaultAccount="true">
            <!-- 密码 -->
            <property name="password">123456</property>
            <!-- 逻辑库名称 -->
            <property name="schemas">TESTDB</property>
            <!-- 默认逻辑库  -->
            <property name="defaultSchema">TESTDB</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>
    

    配置个基本的连接,以便登上去查库中的表

    <?xml version="1.0"?>
    <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
    <mycat:schema xmlns:mycat="http://io.mycat/">
            <!-- 在server.xml中被指定的逻辑库 -->
            <schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1">
            </schema>
            <!-- 对应schema中的 DataNode,database 是真正物理上的数据库  -->
            <dataNode name="dn1" dataHost="host1" database="my_sql_test" />
            <!-- name对应 dataNode 的 datatHost  -->
            <dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                    <!-- 心跳检测  -->
                    <heartbeat>select user()</heartbeat>
                    <!-- 定义写主机 -->
                    <writeHost host="hostM1" url="jdbc:mysql://192.168.81.104:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
            </dataHost>
    </mycat:schema>
    

    登录

    # 登录运维管理窗口
    mysql -u账号 -p密码 -P 9066 -h IP
    # 查看数据库
    show database;
    # 查看命令
    show @@help
    
    # 登录数据窗口
    mysql -u账号 -p密码 -P 8066 -h IP
    # 如
    mysql -umycat -p123456 -P8066 -h192.168.81.104 --default-auth=mysql_native_password
    

    --default-auth=mysql_native_password 是因为 mycat 使用的是5.7以下的加密方式登录

    搭建读写分离

    本文这里使用二进制复制,其余复制可以参考我以前所写Mysql-8.0.2 主从复制(GTID)Mysql-8.0.2 高可用MGR,建议可以先使用二进制方式。

    二进制日志复制

    主机配置

    修改 /etc/my.cnf

    # 主服务器唯一ID
    server-id=1
    # 启用二进制日志
    log-bin=mysql-bin
    # 设置不要复制的数据库(可设置多个)
    binlog-ignore-db=mysql
    # binlog-ignore-db=information_schema
    # 设置需要复制的数据库,不配置代表所有,但切记配置不要复制的库,建议基础库都不排除掉
    binlog-do-db=test1
    # 设置binlog格式
    binlog_format=STATEMENT
    

    binlog 日志有三种格式

    • STATEMENT 会把sql写到日志中,并且带有时间,所以从的时间和主的时间必须一致
    • ROW 不记录写SQL,只记录每行的改变
    • MIXED 切换 STATEMENT 和 ROW

    从机配置

    server-id=2
    # 启用中继日志
    relay-log=mysql-relay
    

    重启主机、从机服务

    授权

    主机
    # 创建用户
    CREATE USER 账号@'%' IDENTIFIED BY '密码';
    # 授权
    GRANT REPLICATION SLAVE ON *.* TO 账号@'%';
    
    # 查看主机的状态
    mysql> show master status;
    +---------------+-----------+--------------+------------------+-----------------------------------------------+
    | File          | Position  | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                             |
    +---------------+-----------+--------------+------------------+-----------------------------------------------+
    | binlog.000021 | 372357328 |    test1     |       mysql      |                                               |
    +---------------+-----------+--------------+------------------+-----------------------------------------------+
    
    • File binlog 文件
    • Position 接入位置,372357328 之前的数据不会复制
    • Binlog_Do_DB 要复制的库
    • Binlog_Ignore_DB 不复制的库
    从机

    建议做之前操作,可以把之前的主从配置信息干掉,也就清洁一下。

    # 停止复制
    stop slave;
    # 重置复制信息
    reset master;
    

    开始配置

    # 配置复制信息
    CHANGE MASTER TO
        MASTER_HOST = '192.168.81.104',
        MASTER_PORT = 3306,
        MASTER_USER = 'repl',
        MASTER_PASSWORD = 'Repl@123',
        MASTER_LOF_FILE='binlog.000021',
        MASTER_LOF_POS=372357328;
    # 开始复制
    start slave;
    # 查看从机状态
    show slave status\G;
    

    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="true" sqlMaxLimit="100" dataNode="dn1">
            </schema>
            <!-- 对应schema中的 DataNode,database 是真正物理上的数据库  -->
            <dataNode name="dn1" dataHost="host1" database="my_sql_test" />
            <!-- name对应 dataNode 的 datatHost  -->
            <dataHost name="host1" maxCon="1000" minCon="10" balance="3" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                    <!-- 心跳检测  -->
                    <heartbeat>select user()</heartbeat>
                    <!-- 定义写主机 -->
                    <writeHost host="hostM1" url="jdbc:mysql://192.168.81.104:3306" user="dev_fqr" password="Dev@fqr2021">
                            <!-- 定义读主机  -->
                            <readHost host="hostS1" url="jdbc:mysql://192.168.81.105:3306" user="dev_fqr" password="Dev@fqr2021" ></readHost>
                    </writeHost>
            </dataHost>
    </mycat:schema>
    

    dataNode 的 database 属性,为MySQL 真实的数据库名称。

    dataHost 的 balance 属性,通过此属性配置读写分离的类型

    • 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" 所有读请求随机的分发到 readHost 执行,writeHost 不负担读压力

    dataHost 的 writeType 属性:

    • writeType="0" 所有写操作发送到配置的第一个 writeHost,第一个挂了切到还生存的第二个
    • writeType="1" 所有操作都随机的发送到配置的 writeHost,1.5以后废弃不推荐

    writeType 重新启动后以切换后的为准,切换记录在配置文件中:dnindex.properties

    dataHost 的 switchType 属性:

    • switchType="1" 默认值,自动切换
    • switchType="-1" 不自动切换
    • switchType="2" 基于MySQL 主从同步的状态决定是否切换

    双主双从

    主(M)1配置

    在以上的主配置中添加

    # 在作为从数据库的时候,有写入操作也要更新二进制日志文件
    log-slave-updates
    # 表示自增长字段每次递增的量,指自己字段的起始值,其默认值是1,取值范围是1~65535(主要是区分M1,M2的自增)
    auto-increment-increment=2
    # 表示自增长字段从哪个数开始,指字段一次递增多少,他的取值范围是1~65535
    auto-increment-offset=1
    
    主(M)2配置

    和主1配置不同的是

    server-id=3
    # 表示自增长字段从哪个数开始,指字段一次递增多少,他的取值范围是1~65535
    auto-increment-offset=2
    

    两台主机都要创建一个复制用户并授权。

    从(S)1,2

    延用之前的从配置,但是注意 server-id 一定不一样,配置完成后需要执行以上从机步骤。关系为 S1->(复制)M1,S2->M2。

    主1主2相互复制

    主1和主2相互执行命令

    CHANGE MASTER TO
        MASTER_HOST = '192.168.81.xxx',
        MASTER_PORT = 3306,
        MASTER_USER = 'repl',
        MASTER_PASSWORD = 'Repl@123',
        MASTER_LOF_FILE='binlog.xxxx',
        MASTER_LOF_POS=xxxxx;
    # 启动
    start slave;
    
    修改MyCat文件
    <?xml version="1.0"?>
    <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
    <mycat:schema xmlns:mycat="http://io.mycat/">
            <!-- 在server.xml中被指定的逻辑库 -->
            <schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1">
            </schema>
            <!-- 对应schema中的 DataNode,database 是真正物理上的数据库  -->
            <dataNode name="dn1" dataHost="host1" database="my_sql_test" />
            <!-- name对应 dataNode 的 datatHost  -->
            <dataHost name="host1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                    <!-- 心跳检测  -->
                    <heartbeat>select user()</heartbeat>
                    <!-- 定义写主1机 -->
                    <writeHost host="hostM1" url="jdbc:mysql://192.168.81.104:3306" user="dev_fqr" password="Dev@fqr2021">
                            <!-- 定义读主1从1机  -->
                            <readHost host="hostS1" url="jdbc:mysql://192.168.81.105:3306" user="dev_fqr" password="Dev@fqr2021" ></readHost>
                    </writeHost>
                    <!-- 定义写主2机 -->
                    <writeHost host="hostM2" url="jdbc:mysql://192.168.81.106:3306" user="dev_fqr" password="Dev@fqr2021">
                            <!-- 定义读主2从2机  -->
                            <readHost host="hostS2" url="jdbc:mysql://192.168.81.107:3306" user="dev_fqr" password="Dev@fqr2021" ></readHost>
                    </writeHost>
            </dataHost>
    </mycat:schema>
    

    垂直切分(分库)

    垂直切分就是微服务的分库操作,如 用户表、积分表等在一个库,被划分为用户服务;订单表、明细表、订单快照表等在一个库中,被划分为订单服务。

    修改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="true" sqlMaxLimit="100" dataNode="dn1">
                <!-- 配置表,所有对 customer 表的操作都会走 dn2 -->
                <table name="customer" dataNode="dn2"></table>
            </schema>
            <!-- 对应schema中的 DataNode,database 是真正物理上的数据库  -->
            <dataNode name="dn1" dataHost="host1" database="orders" />
            <dataNode name="dn2" dataHost="host2" database="users" />
            <!-- name对应 dataNode 的 datatHost  -->
            <dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                    <!-- 心跳检测  -->
                    <heartbeat>select user()</heartbeat>
                    <!-- 定义写主1机 -->
                    <writeHost host="hostM1" url="jdbc:mysql://192.168.81.104:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
            </dataHost>
            <dataHost name="host2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                    <!-- 心跳检测  -->
                    <heartbeat>select user()</heartbeat>
                    <!-- 定义写主1机 -->
                    <writeHost host="hostM1" url="jdbc:mysql://192.168.81.105:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
            </dataHost>
    </mycat:schema>
    

    table 标签的 name 属性,代表实际库中的表,对所有mycat里customer的操作都会走指定的 dataNode.

    水平拆分(分表)

    修改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="true" sqlMaxLimit="100" dataNode="dn1">
                <!-- 配置表 -->
                <table name="customer" dataNode="dn2"></table>
                <!-- 配置表 -->
                <table name="orders" dataNode="dn1,dn3" rule="mod_rule"></table>
            </schema>
            <!-- 对应schema中的 DataNode,database 是真正物理上的数据库  -->
            <dataNode name="dn1" dataHost="host1" database="orders" />
            <dataNode name="dn3" dataHost="host3" database="orders" />
            <dataNode name="dn2" dataHost="host2" database="users" />
            <!-- name对应 dataNode 的 datatHost  -->
            <dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                    <!-- 心跳检测  -->
                    <heartbeat>select user()</heartbeat>
                    <!-- 定义写主1机 -->
                    <writeHost host="hostM1" url="jdbc:mysql://192.168.81.104:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
            </dataHost>
            <dataHost name="host3" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                    <!-- 心跳检测  -->
                    <heartbeat>select user()</heartbeat>
                    <!-- 定义写主1机 -->
                    <writeHost host="hostM1" url="jdbc:mysql://192.168.81.106:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
            </dataHost>
            <dataHost name="host2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                    <!-- 心跳检测  -->
                    <heartbeat>select user()</heartbeat>
                    <!-- 定义写主1机 -->
                    <writeHost host="hostM1" url="jdbc:mysql://192.168.81.105:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
            </dataHost>
    </mycat:schema>
    

    修改rule.xml文件

    <!-- 分片规则 -->
    <tableRule name="mod_rule">
            <rule>
                    <!-- 指定分片的列 -->
                    <columns>customer_id</columns>
                    <!-- 指定分片算法,为function的name -->
                    <algorithm>mod-long</algorithm>
            </rule>
    </tableRule>
    <!-- 具体的分片算法指定类 -->
    <function name="mod-long" class="io.mycat.route.function.PartitionByMod">
            <!-- 数据节点,根据你要分片的数量(机器数量) -->
            <property name="count">2</property>
    </function>
    

    tableRule 表规则,其中name属性就是 schema.xml table标签的 rule属性

    Mycat的分片 "join"

    orders 订单表已经进行分表操作了,和它关联的 orders_detail 订单详情表如何进行 join 查询?其实我们也要对 orders_detail 进行分片操作。

    ER表

    Mycat借鉴了NewSQL领域的新秀 Foundation DB 的设计思路,Foundation DB 创新性的提出了 Table Group 的概念,其将子表的存储位置依赖于主表,并且物理上紧邻存放,因此彻底解决了 JOIN 的效率和性能问题,根据这一思路,提出了基于 E-R 关系的数据分片策略,子表的记录与所关联的父表记录存放在同一个数据分片上。

    修改 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="true" sqlMaxLimit="100" dataNode="dn1">
                <!-- 配置表 -->
                <table name="customer" dataNode="dn2"></table>
                <!-- 配置表 -->
                <table name="orders" dataNode="dn1,dn3" rule="mod_rule">
                    <!-- join表的name,该表的(primaryKey)主键,该表的(joinKey)外键,对应主表的(parentKey)ID -->
                    <childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
                </table>
            </schema>
            <!-- 对应schema中的 DataNode,database 是真正物理上的数据库  -->
            <dataNode name="dn1" dataHost="host1" database="orders" />
            <dataNode name="dn3" dataHost="host3" database="orders" />
            <dataNode name="dn2" dataHost="host2" database="users" />
            <!-- name对应 dataNode 的 datatHost  -->
            <dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                    <!-- 心跳检测  -->
                    <heartbeat>select user()</heartbeat>
                    <!-- 定义写主1机 -->
                    <writeHost host="hostM1" url="jdbc:mysql://192.168.81.104:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
            </dataHost>
            <dataHost name="host3" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                    <!-- 心跳检测  -->
                    <heartbeat>select user()</heartbeat>
                    <!-- 定义写主1机 -->
                    <writeHost host="hostM1" url="jdbc:mysql://192.168.81.106:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
            </dataHost>
            <dataHost name="host2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                    <!-- 心跳检测  -->
                    <heartbeat>select user()</heartbeat>
                    <!-- 定义写主1机 -->
                    <writeHost host="hostM1" url="jdbc:mysql://192.168.81.105:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
            </dataHost>
    </mycat:schema>
    

    childTable 为要join的子表

    全局表

    在分片的情况下,当业务表因为规模而进行分片以后,业务表与这些附属的字典表之间的关联,就成了比较棘手的问题,考虑到字典表具有以下几个特性:

    1. 变动部频繁
    2. 数据量总体变化不大
    3. 数据规模不大,很少有超过数十万条记录

    鉴于此,Mycat定义了一种特殊的表,称之为"全局表",全局表具有以下特性:

    1. 全局表插入、更新操作会实时在所有节点上执行,保持各个分片的数据一致性
    2. 全局表的查询操作,只从一个节点获取
    3. 全局表可以跟任何一个表进行JOIN操作

    将字典或者符合字典特性的一些表定义为全局表,则从另外一个方面,很好的解决了数据JOIN的难题。通过全局表+基于E-R关系的分片策略,MyCat 可以满足 80%以上的企业应用开发。

    <?xml version="1.0"?>
    <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
    <mycat:schema xmlns:mycat="http://io.mycat/">
            <!-- 在server.xml中被指定的逻辑库 -->
            <schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1">
                <!-- 配置表 -->
                <table name="customer" dataNode="dn2"></table>
                <!-- 配置表 -->
                <table name="orders" dataNode="dn1,dn3" rule="mod_rule">
                    <!-- join表的name,该表的(primaryKey)主键,该表的(joinKey)外键,对应主表的(parentKey)ID -->
                    <childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
                </table>
                <!-- 全局表,name="表名"  dataNode="指定节点" type="全局global" -->
                <table name="dict_order_type" dataNode="dn1,dn2,dn3" type="global"></table>
            </schema>
            <!-- 对应schema中的 DataNode,database 是真正物理上的数据库  -->
            <dataNode name="dn1" dataHost="host1" database="orders" />
            <dataNode name="dn3" dataHost="host3" database="orders" />
            <dataNode name="dn2" dataHost="host2" database="users" />
            <!-- name对应 dataNode 的 datatHost  -->
            <dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                    <!-- 心跳检测  -->
                    <heartbeat>select user()</heartbeat>
                    <!-- 定义写主1机 -->
                    <writeHost host="hostM1" url="jdbc:mysql://192.168.81.104:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
            </dataHost>
            <dataHost name="host3" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                    <!-- 心跳检测  -->
                    <heartbeat>select user()</heartbeat>
                    <!-- 定义写主1机 -->
                    <writeHost host="hostM1" url="jdbc:mysql://192.168.81.106:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
            </dataHost>
            <dataHost name="host2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                    <!-- 心跳检测  -->
                    <heartbeat>select user()</heartbeat>
                    <!-- 定义写主1机 -->
                    <writeHost host="hostM1" url="jdbc:mysql://192.168.81.105:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
            </dataHost>
    </mycat:schema>
    

    常用分片规则

    1. 取模,此规则为对分片字段求模运算。也是水平分表最常用规则
    2. 分片枚举,通过在配置文件中配置可能的枚举ID,自己配置分片,本规则适用于特定的场景,比如有些有任务需要按照省份或区县来做报错,而全国省份区县固定的,这类业务使用本条规则。
    # 修改 schema.xml 
    <table name="orders" dataNode="dn1,dn3" rule="sharding_by_intfile"></table>
    
    # 修改 rule.xml
    <!-- 分片规则 -->
    <tableRule name="mod_rule">
            <rule>
                    <!-- 指定分片的列 -->
                    <columns>areacode</columns>
                    <!-- 指定分片算法,为function的name -->
                    <algorithm>hash-int</algorithm>
            </rule>
    </tableRule>
    <!-- 具体的分片算法指定类 -->
    <function name="hash-int" class="io.mycat.route.function.PartitionByFileMap">
            <!-- 标识配置文件 -->
            <property name="mapFile">partition-hash-int.txt</property>
            <!-- type:0为int类型,非0为String类型 -->
            <property name="type">1</property>
            <!-- 默认节点:小于 0 表示不设置默认节点,大于等于0表示设置默认节点,
              配置文件找不到枚举值,就让他路由到默认节点找,没有设置默认节点,在文件找不到,就报错 -->
            <property name="defaultNode">0</property>
    </function>
    
    # 修改partition-hash-int.txt文件,添加如下
    # 找到区域编号110的,认为是第1个数据节点(dn1)
    110=0
    # 找到区域编号120的,认为是第2个数据节点(dn3)
    120=1
    
    1. 范围约定,此分片适用于,提前规划好分片字段某个范围属于哪个分片。
    # 修改 schema.xml
    <table name="payment_info" dataNode="dn1,dn2" rule="auto_sharding_long"></table>
    
    # 修改 rule.xml
    <!-- 分片规则 -->
    <tableRule name="auto_sharding_long">
            <rule>
                    <!-- 指定分片的列 -->
                    <columns>order_id</columns>
                    <!-- 指定分片算法,为function的name -->
                    <algorithm>rang-long</algorithm>
            </rule>
    </tableRule>
    <!-- 具体的分片算法指定类 -->
    <function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong">
            <!-- 定义范围 -->
            <property name="mapFile">autopartition-long.txt</property>
            <!-- 不在范围找默认节点 -->
            <property name="defaultNode">0</property>
    </function>
    # 修改autopartition-long.txt文件,不在配置文件默认找第0个节点(dn1)
    # 0-102 到第1个节点(dn1)
    0-102=0
    # 103-200到第2个节点(dn2)
    103-200=1
    
    1. 按时间分片
    # 修改 schema.xml
    <table name="login_info" dataNode="dn1,dn2" rule="sharding_by_date"></table>
    
    # 修改 rule.xml
    <!-- 分片规则 -->
    <tableRule name="sharding_by_date">
            <rule>
                    <!-- 指定分片的列 -->
                    <columns>create_time</columns>
                    <!-- 指定分片算法,为function的name -->
                    <algorithm>partbyday</algorithm>
            </rule>
    </tableRule>
    <!-- 具体的分片算法指定类 -->
    <function name="partbyday" class="io.mycat.route.function.PartitionByDate">
            <!-- 格式化时间 -->
            <property name="dateFormat">yyyy-MM-dd</property>
            <!-- 开始日期 -->
            <property name="sBeginDate">2021-01-01</property>
            <!-- 结束日期,结束日期一定是 节点*分区天数,
                 也就是我2个节点,分区天数3,1-3号到dn1,3-6号到dn2,6-9号的话又分到dn1;
                 如果不设置结束日期,则6-9号要分到dn3,没有dn3则报错
             -->
            <property name="sEndDate">2021-01-31</property>
            <!-- 分区天数 -->
            <property name="sPartionDay">2</property>
    </function>
    

    全局序列

    在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一,为此,Mycat 提供了全局的 sequence ,并且提供了包含本地配置和数据库配置等多种实现方式

    本地文件

    此方式 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 会再向数据库要一次。

    注意:
    MYCAT_SEQUENCE 表和以下的 3 个function,需要放在同一个节点上。function请直接在具体节点的数据库上执行,如果执行的时候报:
    you might want to use the less safe log_bin_trust_function_creators variable
    需要对数据库做如下设置:
    windows下my.ini[mysqld]加上log_bin_trust_function_creators=1
    linux下/etc/my.cnf下my.ini[mysqld]加上log_bin_trust_function_creators=1
    修改完后,即可在mysql数据库中执行下面的函数。
    
    1. 创建全局序列表
    CREATE TABLE MYCAT_SEQUENCE(
        - sequence名称,最好为表名
        name varchar(50) NOT NULL,
        -  当前value,起始值
        current_value INT NOT NULL,
        -  增长步长! 可理解为mycat在数据库中一次读取多少个 sequence. 当这些用完后, 下次再从数据库中读取。
        increment INT NOT NULL DEFAULT 100,
        PRIMARY KEY(name)
    )ENGINE=INNODB;
    
    # 往表中插入数据
    INSERT INTO MYCAT_SEQUENCE(name,current_value,increment) VALUES ('orders',1, 1000);
    INSERT INTO MYCAT_SEQUENCE(name,current_value,increment) VALUES ('users',1, 1000);
    
    2. 创建全局序列所需函数
    - 获取当前sequence的值 (返回当前值,增量)
    DROP FUNCTION IF EXISTS mycat_seq_currval;
    DELIMITER
    CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET utf-8
    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;
    
    - 设置sequence值
    DROP FUNCTION IF EXISTS mycat_seq_setval;
    DELIMITER
    CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50),value INTEGER) RETURNS varchar(64) CHARSET utf-8
    DETERMINISTIC
    BEGIN
    UPDATE MYCAT_SEQUENCE
    SET current_value = value
    WHERE name = seq_name;
    RETURN mycat_seq_currval(seq_name);
    END
    DELIMITER;
    
    – 获取下一个sequence值
    DROP FUNCTION IF EXISTS mycat_seq_nextval;
    DELIMITER
    CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET utf-8
    DETERMINISTIC
    BEGIN
    UPDATE MYCAT_SEQUENCE
    SET current_value = current_value + increment WHERE name = seq_name;
    RETURN mycat_seq_currval(seq_name);
    END
    DELIMITER;
    
    3. sequence_db_conf.properties相关配置,指定sequence相关配置在哪个节点上
    orders=dn1
    users=dn1
    
    4. server.xml配置:
    <system>
        <!-- 0本地文件 1数据库方式 2时间戳方式 -->
        <property name="sequnceHandlerType">1</property>
    </system>
    
    5.插入数据
    insert into orders(id,title,price) values(next value for MYCATSEQ_ORDERS,'手机','2000.00');
    insert into (id,name) values(next value for MYCATSEQ_USERS,'张三');
    

    相关文章

      网友评论

        本文标题:MyCat 1.6.7(一)MySQL高可用及分库分表

        本文链接:https://www.haomeiwen.com/subject/ntcnxrtx.html