一、Mycat
1、介绍
Mycat是开源的、活跃的、基于Java语言编写的MySQL数据库中间件。可以像使用mysql一样来使用mycat,对于开发人员来说根本感觉 不到mycat的存在。(伪装协议)
项目中,只需要将数据库连接换为mycat就可以了,其他都不需要动
文档:https://www.yuque.com/books/share/6606b3b6-3365-4187-94c4-e51116894695/swswoy
2、安装
本文使用的版本是 1.6.7.4
需要安装 jdk ,并配置环境变量
下载后上传到服务器并解压
tar -vxzf Mycat-server-1.6.7.4-release-20210913163959-linux.tar.gz
解压后的文件结构
bin : 存放可执行文件,用于启动停止mycat
conf:存放mycat的配置文件
lib:存放mycat的项目依赖包(jar)
logs:存放mycat的日志文
如果使用的 mysql 版本较高,需要手动替换 lib 文件夹下的 mysql-conector-java 依赖,如果mysql不是使用8.0以上的,就不需要替换
然后需要对刚上传的jar进行授权
chmod 777 mysql-connector-java-8.0.17.jar
安装完成
3、核心概念

4、基本使用-水平分表
案例:对 tb_order 这张表进行水平分库,一个库在阿里云服务器,一个在阿里云RDS数据库。
确保 MySQL 数据库配置文件中已配置 lower_case_table_names=1
,不区分大小写。
(1)分片配置
修改 mycat 安装目录下的conf下的 schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 逻辑库 -->
<!-- name="库名" checkSQLschema="true" sqlMaxLimit="100" -->
<!-- table name 属性使用逗号分割配置多个表,即多个表使用这个配置 -->
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100">
<!-- 逻辑表 dataNode=分片列表 rule=分片规则 -->
<table name="tb_order" dataNode="dn1,dn2" rule="auto-sharding-long" />
</schema>
<!-- 分片,dataHost=节点主机 -->
<dataNode name="dn1" dataHost="aliyun" database="my_test2" />
<dataNode name="dn2" dataHost="rds" database="my_test2" />
<dataHost name="aliyun" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳 -->
<heartbeat>select user()</heartbeat>
<!-- 数据库连接信息,注意此处url参数之间用&取代& -->
<writeHost host="hostS1" url="jdbc:mysql://120.79.69.164:3306/my_test2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"
user="root" password="Root_211964" />
</dataHost>
<dataHost name="rds" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳 -->
<heartbeat>select user()</heartbeat>
<!-- 数据库连接信息 -->
<writeHost host="hostS1" url="jdbc:mysql://rm-wz96qu13c8715za9kfo.mysql.rds.aliyuncs.com:3306/my_test2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"
user="root" password="Root_211964" />
</dataHost>
</mycat:schema>
(2)权限配置
修改 mycat 安装目录下的conf下的 server.xml
<!-- 确保这个id的生成策略为2 ,表示为根据时间戳 -->
<property name="sequnceHandlerType">2</property>
...
<!-- 配置用户权限 -->
<user name="root" defaultAccount="true">
<property name="password">Root_211964</property>
<!-- 用户可访问的逻辑库,多个库之间用逗号隔开 -->
<property name="schemas">TESTDB</property>
</user>
<user name="user">
<property name="password">Root_211964</property>
<property name="schemas">TESTDB</property>
<!-- 只读 -->
<property name="readOnly">true</property>
</user>
...
(3)分片规则
逻辑表的分片规则:rule="auto-sharding-long"
,默认节点数为 3,当我们的节点数不等于3时,会报错
Caused by:
io.mycat.config.util.ConfigException:
Illegal table conf :
table [ TB_ORDER ] rule function [ rang-long ] partition size : 3 > table datanode size : 2, please make sure table datanode size = function partition size
修改 rule.xml
<!-- 添加defaultNode ,设定默认节点为0 ,不设定就会报错 -->
<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
,设定分片大小和规则
vim ./conf/autopartition-long.txt
#默认是三个,我们需要删除最后一个,不然就会报错,说节点少了
#K=1000条记录,M=10000条记录,那么下面三个配置就是0~500万的记录会存在数据库节点1的表中,500万~1000万会存在节点2的表中
0-500M=0
500M-1000M=1
该分片规则是用 id 字段判断,存储的数据 id 小于等于 500万存到第一个节点,大于500万小于等于1000万存到第二个节点,如果只有2节点,存储的id大于1000万时,报错。
(4)测试
在两台服务器创建对应的数据库,无需创建表
切换到Mycat的安装目录,执行如下指令,启动Mycat:
# 启动命令
bin/mycat start
# 停止命令
bin/mycat stop
Mycat启动之后,占用端口号 8066。
启动完毕之后,可以查看logs目录下的启动日志,查看Mycat是否启动完成。
tail -f logs/wrapper.log
连接 mycat ,和连接mysql一样(也可以使用navicat)
mysql -h 主机IP -P 8066 -uroot -p密码
在 Mycat 创建表
CREATE TABLE `tb_order` (
`id` bigint(1) NOT NULL,
`user_name` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
5、schema.xml 配置
(1)schema标签
<!-- 逻辑库 -->
<!-- table name 属性使用逗号分割配置多个表,即多个表使用这个配置 -->
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100">
<!-- 逻辑表 dataNode=分片列表 rule=分片规则 -->
<table name="tb_order" dataNode="dn1,dn2" rule="auto-sharding-long" />
</schema>
schema 标签用于定义MyCat实例中的逻辑库, 一个MyCat实例中, 可以有多个逻辑库, 可以通过 schema 标签来划分不同的逻辑库。
MyCat中的逻辑库的概念, 等同于MySQL中的database概念, 需要操作某个逻辑库下的表时, 也需要切换逻辑库(use xxx)。
核心属性:
- name:指定自定义的逻辑库库名
- checkSQLschema:在SQL语句操作时指定了数据库名称,执行时是否自动去除;true:自动去除,false:不自动去除
- sqlMaxLimit:如果未指定limit进行查询,列表查询模式查询多少条记录
(2)table标签
table 标签定义了MyCat中逻辑库schema下的逻辑表, 所有需要拆分的表都需要在table标签中定义。
table 标签核心属性:
- name:定义逻辑表表名,在该逻辑库下唯一
- dataNode:定义逻辑表所属的dataNode,该属性需要与dataNode标签中name对应;多个dataNode逗号分隔
- rule:分片规则的名字,分片规则名字是在rule.xml中定义的
- primaryKey:逻辑表对应真实表的主键
- type:逻辑表的类型,目前逻辑表只有全局表和普通表,如果未配置,就是普通表;全局表,配置为global
(3)dataNode标签
<!-- 分片,dataHost=节点主机 -->
<dataNode name="dn1" dataHost="aliyun" database="my_test2" />
dataNode标签中定义了MyCat中的数据节点, 也就是我们通常说的数据分片。一个dataNode标签就是一个独立的数据分片。
核心属性:
- name:定义数据节点名称
- dataHost:数据库实例主机名称,引用自dataHost 标签中name属性
- database:定义分片所属数据库
(4)dataHost标签
<dataHost name="aliyun" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳 -->
<heartbeat>select user()</heartbeat>
<!-- 数据库连接信息,注意此处url参数之间用&取代& -->
<writeHost host="hostS1" url="jdbc:mysql://120.79.69.164:3306/my_test2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"
user="root" password="Root_211964" />
</dataHost>
该标签在MyCat逻辑库中作为底层标签存在, 直接定义了具体的数据库实例、读写分离、心跳语句。
核心属性:
name:唯一标识,供上层标签使用(dataNode标签)
maxCon/minCon:最大连接数/最小连接数
balance:负载均衡策略,取值 0,1,2,3 (读写分离处使用)
writeType:写操作分发方式
- 0:写操作转发到第一个writeHost,第一个挂了,切换到第二个;
- 1:写操作随机分发到配置的writeHost
dbDriver:数据库驱动,支持native、jdbc
6、rule.xml 配置
rule.xml中定义所有拆分表的规则, 在使用过程中可以灵活的使用分片算法, 或者对同一个分片算法使用不同的参数, 它让分片过程可配 置化。主要包含两类标签:tableRule、Function。
(1)tableRule 标签
<tableRule name="auto-sharding-long">
<rule>
<!-- 根据id字段进行分片 -->
<columns>id</columns>
<!-- 分片算法,引用Function标签 -->
<algorithm>rang-long</algorithm>
</rule>
</tableRule>
(2)function标签
<function name="rang-long"
class="io.mycat.route.function.AutoPartitionByLong">
<!-- 关联的可配置参数的外部文件 -->
<property name="mapFile">autopartition-long.txt</property>
<property name="defaultNode">0</property>
</function>
7、server.xml 配置
server.xml配置文件包含了MyCat的系统配置信息,主要有两个重要的标签:system、user。
(1)system 标签
对应的系统配置项及其含义,参考资料。
<system>
<property name="nonePasswordLogin">0</property> <!-- 0为需要密码登陆、1为不需要密码登陆 ,默认为0,设置为1则需要指定默认账户-->
<property name="ignoreUnknownCommand">0</property><!-- 0遇上没有实现的报文(Unknown command:),就会报错、1为忽略该报文,返回ok报文。
在某些mysql客户端存在客户端已经登录的时候还会继续发送登录报文,mycat会报错,该设置可以绕过这个错误-->
<property name="useHandshakeV10">1</property>
<property name="removeGraveAccent">1</property>
<property name="useSqlStat">0</property> <!-- 1为开启实时统计、0为关闭 -->
<property name="useGlobleTableCheck">0</property> <!-- 1为开启全加班一致性检测、0为关闭 -->
<property name="sqlExecuteTimeout">300</property> <!-- SQL 执行超时 单位:秒-->
...
</system>
(2)user 标签
配置用户及其权限
8、垂直分库
(1)基本配置
<!-- =================垂直分库======================================== -->
<schema name="my_test3" checkSQLschema="true" sqlMaxLimit="100">
<!-- 逻辑表 dataNode=分片列表 rule=分片规则 -->
<table name="tb_shopping" dataNode="aliyun" primaryKey="id" />
<table name="tb_address" dataNode="rds" primaryKey="id" />
</schema>
<!-- 分片,dataHost=节点主机 -->
<dataNode name="aliyun" dataHost="aliyun" database="my_test2" />
<dataNode name="rds" dataHost="rds" database="my_test2" />
primaryKey:表的主键,这里不需要 rule 属性,rule 属性时用来定义分片规则的,这里一张表只存在一个数据库,不需要分片
dataHost 标签同上,这里就不写了。
(2)创建表
只需在 Mycat 创建表,Mycat 即会将表创建到指定的数据库。
创建没有配置的表会报错。
表在命令行可以显示全部配置的表,但用 navicat 等工具每次只能展示其中一个mysql数据库中的表,包括mysql节点中未配置的表,navicat刷新一下,有展示的是另一个mysql节点的全部表。好奇怪啊!!!
(3)多表联查
在 Mcat 中使用多表联查时:
- 多张表在同一个分片中,可以执行
- 多张表在不同分片中,不能执行,报错
此时应该将一些多个业务中都会用到的表,如字典表,设置为全局表
(4)配置全局表
<schema name="my_test3" checkSQLschema="true" sqlMaxLimit="100">
<table name="tb_dict" dataNode="aliyun,rds" primaryKey="id" type="global" />
</schema>
当执行创建表的语句时,会在全局表所配置的所有分片中创建表
对全局表进行插入数据时,也会对所有分片进行插入
9、水平分表
在业务系统中, 有一张表(日志表), 业务系统每天都会产生大量的日志数据, 单台服务器的数据存储及处理能力是有限的, 可以对数据库表 进行拆分。
定义一个逻辑表,拥有多个节点
水平分表核心是分片规则
配置完启动mycat,需要优先创建数据库
10、分片规则
(1)范围分片
根据指定的字段及其配置的范围与数据节点的对应情况, 来决定该数据属于哪一个分片。
注意:只能指定数字类型的字段
如:id 为0到500万存放到第一个节点
500万到1000万存放到第二个节点
1000万到1500万存放到第三个节点
schema.xml 中配置逻辑表
<table name="tb_order" dataNode="aliyun,rds" rule="auto-sharding-long" />
rule.xml 中配置相关分片字段
<tableRule name="auto-sharding-long">
<rule>
<columns>id</columns>
<algorithm>rang-long</algorithm>
</rule>
</tableRule>
(2)取模
根据指定的字段值与节点数量进行求模运算(分散存储),根据运算结果, 来决定该数据属于哪一个分片。
注意:只能指定数字类型的字段
如:id 为 9,模于 节点数 3,得到结果 0,落在第一个节点
id 为 10,模于 节点数 3,得到结果 1,落在第二个节点
id 为 11,模于 节点数 3,得到结果 2,落在第三个节点
(3)一致性 hash
所谓一致性哈希,相同的哈希因子计算值总是被划分到相同的分区表中,不会因为分区节点的增加而改变原来数据的分区位置。
注意:指定字段不一定要是数字
(4)枚举
通过在配置文件中配置可能的枚举值, 指定数据分布到不同数据节点上, 本规则适用于按照省份、性别、状态拆分数据等业务 。
(5)应用指定
运行阶段由应用自主决定路由到那个分片, 直接根据字符子串(必须是数字)计算分片号。
(6)固定分片hash算法
该算法类似于十进制的求模运算,但是为二进制的操作,例如,取id 的二进制低10 位 与 1111111111 进行位& 运算。
0 或 1 和 1 进行 & 运算之后,值为它本身。
特点:
➢ 如果是求模,连续的值,分别分配到各个不同的分片;但是此算法会将连续的值可能分配到相同的分片,降低事务处理的难度。
➢ 可以均匀分配,也可以非均匀分配。
➢ 分片字段必须为数字类型。
(7)字符串hash解析
截取字符串中的指定位置的子字符串, 进行hash算法,算出分片。
(8)按(天)日期分片
(9)自然月
使用场景为按照月份来分片, 每个自然月为一个分片。
二、MyCat读写分离
读写分离,简单地说是把对数据库的读和写操作分开,以对应不同的数据库服务器。主数据库提供写操作,从数据库提供读操作,这样能有效 地减轻单台数据库的压力。
通过MyCat即可轻易实现上述功能,不仅可以支持MySQL,也可以支持Oracle和SQL Server。
1、一主一从
搭建方式见主从复制
原理:MySQL的主从复制,是基于二进制日志(binlog)实现的。
配置:MyCat控制后台数据库的读写分离和负载均衡由schema.xml文件datahost标签的balance属性控制。
需要注意的是 datahost
标签的属性 balance
是用于配置负载均衡策略,目前有如下4种取值
参数值 | 含义 |
---|---|
0 | 不开启读写分离机制 所有读操作都发送到当前可用的writeHost 上 |
1 | 全部的 readHost 与备用的writeHost 都参与 select 语句的负载均衡(主要针对于双主双从模式) |
2 | 所有的读写操作都随机在writeHost 、readHost 上分发 |
3 | 所有的读请求随机分发到writeHost 对应的 readHost 上执行,writeHost 不负担读压力 |
1 和 3 为读写分离
配置好以上配置后,即可实现读写分离
问题:主节点Master宕机之后,业务系统就只能够读,而不能写入数据了。
2、双主双从搭建
一个主机Master1 用于处理所有写请求,它的从机 Slave1 和另一台主机Master2 还有它的从机Slave2 负责所有读请求。当 Master1 主机宕机后,Master2 主机负责写请求,Master1 、Master2 互为备机。架构图如下:
其实双主双从根本上是 3 组主从复制:
- 主库1、从库1 — 单向主从
- 主库2、从库2 — 单向主从
- 主库1、主库2 — 双向主从
(1)主库配置
修改配置文件/etc/my.cnf
2 台主库配置有且只有 server-id
,不同
# mysql 服务id,保证整个集群环境中唯一,取值范围:1 到 2的31次方减1,默认为1
server-id=1
# 指定要同步的数据库
binlog-db=db01
#在作为从数据库的时候,有写入操作也要更新二进制日志文件
log-slave-updates
重启数据库
systemctl restart mysqld
两台主库创建账户并授权
# 创建 rewind 用户,并设置密码,%表示该用户可在任意主机连接mysql服务
create user 'rewind'@'%' identified with mysql_native_password by '123456';
# 为 rewind 用户分匹配主从复制权限
grant replication slave on *.* to 'rewind'@'%';
通过指令,查看两台主库的二进制日志坐标
show master status;
字段含义说明:
- file : 从哪个日志文件开始推送日志文件
- position : 从哪个位置开始推送日志
- binlog_ignore_db : 指定不需要同步的数据库
(2)从库配置
修改配置文件/etc/my.cnf
# mysql 服务id,保证整个集群环境中唯一,取值范围:1 到 2的31次方减1,默认为1
server-id=3
重启后,两台从库配置关联的主库
CHANGE MASTER TO MASTER_HOST='XXX.XXX',MASTER_USER='XXX',MASTER_PASSWORD='XXX',MASTER_LOG_FILE='mysql-bin.000988',MASTER_LOG_POS=386021;
需要注意slave1对应的是master1,slave2对应的是master2。
启动两台从库主从复制,查看从库状态
# 启动 ,8.0.22 之前
start slave;
# 查看状态,8.0.22 之前
show slave status\G
查看系统返回信息中 Slave_IO_Running 和 Slave_SQL_Running 的状态是否为 Yes。
(3)双主同步配置
Master2 复制 Master1,Master1 复制 Master2。
CHANGE MASTER TO MASTER_HOST='XXX.XXX',MASTER_USER='XXX',MASTER_PASSWORD='XXX',MASTER_LOG_FILE='mysql-bin.000988',MASTER_LOG_POS=386021;
启动两台主库库主从复制,查看状态
# 启动 ,8.0.22 之前
start slave;
# 查看状态,8.0.22 之前
show slave status\G
3、双主双从读写分离
MyCat控制后台数据库的读写分离和负载均衡由schema.xml文件datahost标签的balance属性控制,通过writeType及switchType来完成失 败自动切换的。
登录MyCat,测试查询及更新操作,判定是否能够进行读写分离,以及读写分离的策略是否正确。
当主库挂掉一个之后,是否能够自动切换。