数据库信息
创建 db lan1 和 lane2 分别创建表 position
CREATE TABLE `position` (`Id` bigint(11) NOT NULL AUTO_INCREMENT,`name` varchar(256) DEFAULT NULL,`salary` varchar(50) DEFAULT NULL,`city` varchar(256) DEFAULT NULL,PRIMARY KEY (`Id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Mycat 下载
提示:需要先安装 jdk,同 sharding proxy
吐槽一下:真是一个小天才啊,macos 一个版本、linux 一个版本、unix 一个版本
一开始我下载的 linux 不行,后来才发现竟然区分 mac 和 linux
下载 Mycat-server 工具包

进入 mycat/bin,启动 Mycat 命令如下
启动命令:./mycat start停止命令:./mycat stop重启命令:./mycat restart查看状态:./mycat status
Mycat 配置
server.xml
<!--只是修改了下主键生成规则从 1 改成了 0--><property name="sequenceHandlerType">0</property><!--用户改成了root和test--><user name="root" defaultAccount="true"><property name="password">root</property><property name="schemas">lane_db</property><property name="defaultSchema">lane_db</property></user><user name="test"><property name="password">test</property><property name="schemas">lane_db</property><property name="readOnly">true</property><property name="defaultSchema">lane_db</property></user>
完整版 server.xml
<?xml version="1.0" encoding="UTF-8"?><!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="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 执行超时 单位:秒--><property name="sequenceHandlerType">0</property> <!--0配置文件指定生成,1数据库生成--><!--<property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property>INSERT INTO `travelrecord` (`id`,user_id) VALUES ('next value for MYCATSEQ_GLOBAL',"xxx");--><!--必须带有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><!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2为不过滤分布式事务,但是记录分布式事务日志--><property name="handleDistributedTransactions">0</property><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><!--如果为0的话,涉及多个DataNode的catlet任务不会跨线程执行--><property name="parallExecute">0</property></system><user name="root" defaultAccount="true"><property name="password">root</property><property name="schemas">lane_db</property><property name="defaultSchema">lane_db</property></user><user name="test"><property name="password">test</property><property name="schemas">lane_db</property><property name="readOnly">true</property><property name="defaultSchema">lane_db</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="lane_db" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1"><!-- auto sharding by id (long) --><!--splitTableNames 启用<table name 属性使用逗号分割配置多个表,即多个表使用这个配置--><!--fetchStoreNodeByJdbc 启用ER表使用JDBC方式获取DataNode--><table name="position" primaryKey="id" dataNode="dn1,dn2" rule="mod-long" autoIncrement="true" fetchStoreNodeByJdbc="true"><!-- <childTable name="customer_addr" primaryKey="id" joinKey="customer_id" parentKey="id"> </childTable> --></table></schema><!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"/> --><dataNode name="dn1" dataHost="localhost1" database="lane1" /><dataNode name="dn2" dataHost="localhost1" database="lane2" /><dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"><heartbeat>select user()</heartbeat><!-- can have multi write hosts --><writeHost host="hostM1" url="jdbc:mysql://localhost:3306" user="root"password="root"></writeHost><!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> --></dataHost></mycat:schema>
rule.xml 修改下为对 2 取模,对应于 schema 配置的分片规则 mod-long
<tableRule name="mod-long"><rule><columns>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>
Mycat 启动
cd /Users/dulane/software/mycat/mycat-mac/bin./mycat startStarting Mycat-server...

Mycat 测试
测试下 test 用户
访问 mysql
mysql -utest -ptest -h127.0.0.1 -P8066
具体操作
~ mysql -utest -h 127.0.0.1 -P 8066 -pEnter password:mysql> show databases;+----------+| DATABASE |+----------+| lane_db |+----------+1 row in set (0.01 sec)mysql> use lane_dbDatabase changedmysql> show tables;+-------------------+| Tables in lane_db |+-------------------+| position |+-------------------+1 row in set (0.00 sec)mysql> select * from position;+--------------------+--------+---------+-----------+| Id | name | salary | city |+--------------------+--------+---------+-----------+| 624565542031720448 | lisi1 | 1000000 | shanghai || 624565542786695168 | lisi3 | 1000000 | shanghai || 624565543113850880 | lisi5 | 1000000 | shanghai || 624565543436812288 | lisi7 | 1000000 | shanghai || 624565543730413568 | lisi9 | 1000000 | shanghai || 624565543956905984 | lisi11 | 1000000 | shanghai || 624565544183398400 | lisi13 | 1000000 | shanghai || 624565544422473728 | lisi15 | 1000000 | shanghai || 624565544728657920 | lisi17 | 1000000 | shanghai || 624565544971927552 | lisi19 | 1000000 | shanghai || 625037191625572352 | root1 | 1000000 | beijing || 625390806554902528 | lucy | 21000 | hangzhou || 625399491796664320 | sky9 | 100000 | guangzhou || 624565542639894529 | lisi2 | 1000000 | shanghai || 624565542954467329 | lisi4 | 1000000 | shanghai || 624565543281623041 | lisi6 | 1000000 | shanghai || 624565543592001537 | lisi8 | 1000000 | shanghai || 624565543852048385 | lisi10 | 1000000 | shanghai || 624565544057569281 | lisi12 | 1000000 | shanghai || 624565544309227521 | lisi14 | 1000000 | shanghai || 624565544581857281 | lisi16 | 1000000 | shanghai || 624565544833515521 | lisi18 | 1000000 | shanghai || 624565545101950977 | lisi20 | 1000000 | shanghai || 625390705467981825 | tom | 20000 | shanghai || 625399491637280769 | sky4 | 100000 | guangzhou |+--------------------+--------+---------+-----------+25 rows in set (0.08 sec)mysql> delete from position where name ='tom';ERROR 1495 (HY000): User readonlymysql>
测试下 root 用户
具体操作
➜ ~ mysql -uroot -h 127.0.0.1 -P 8066 -pmysql> use lane_db;Database changedmysql> insert into position values(1,'yangqian1','1000000','olmpic');ERROR 1064 (HY000): insert must provide ColumnListmysql> insert into position(id,name,salary,city) values(1,'yangqian1','1000000','olmpic');Query OK, 1 row affected (0.02 sec)OK!mysql> insert into position(id,name,salary,city) values(2,'yangqian2','1000000','olmpic');Query OK, 1 row affected (0.02 sec)OK!mysql> select * from position;+--------------------+-----------+---------+-----------+| Id | name | salary | city |+--------------------+-----------+---------+-----------+| 2 | yangqian2 | 1000000 | olmpic || 624565542031720448 | lisi1 | 1000000 | shanghai || 624565542786695168 | lisi3 | 1000000 | shanghai || 624565543113850880 | lisi5 | 1000000 | shanghai || 624565543436812288 | lisi7 | 1000000 | shanghai || 624565543730413568 | lisi9 | 1000000 | shanghai || 624565543956905984 | lisi11 | 1000000 | shanghai || 624565544183398400 | lisi13 | 1000000 | shanghai || 624565544422473728 | lisi15 | 1000000 | shanghai || 624565544728657920 | lisi17 | 1000000 | shanghai || 624565544971927552 | lisi19 | 1000000 | shanghai || 625037191625572352 | root1 | 1000000 | beijing || 625390806554902528 | lucy | 21000 | hangzhou || 625399491796664320 | sky9 | 100000 | guangzhou || 1 | yangqian1 | 1000000 | olmpic || 624565542639894529 | lisi2 | 1000000 | shanghai || 624565542954467329 | lisi4 | 1000000 | shanghai || 624565543281623041 | lisi6 | 1000000 | shanghai || 624565543592001537 | lisi8 | 1000000 | shanghai || 624565543852048385 | lisi10 | 1000000 | shanghai || 624565544057569281 | lisi12 | 1000000 | shanghai || 624565544309227521 | lisi14 | 1000000 | shanghai || 624565544581857281 | lisi16 | 1000000 | shanghai || 624565544833515521 | lisi18 | 1000000 | shanghai || 624565545101950977 | lisi20 | 1000000 | shanghai || 625390705467981825 | tom | 20000 | shanghai || 625399491637280769 | sky4 | 100000 | guangzhou |+--------------------+-----------+---------+-----------+27 rows in set (0.01 sec)
查看下 database ,可以看到杨倩选手成功获得了东京奥运会首金,跑题了,可以看到 id 是按照 模 2 进行的分库操作,分别添加到了库 lane1 和 lane2 里面了

分片规则
分片规则还是蛮多的,这里 rule 修改为 auto-sharding-long
<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>
修改 schema.xml 下的 分片规则如下
<table name="position" primaryKey="id" dataNode="dn1,dn2" rule="auto-sharding-long"autoIncrement="true" fetchStoreNodeByJdbc="true"></table>

再修改下 autopartition-long.txt 文件
因为我们只有两个数据库,这里注释掉 1000M-1500M
# range start-end ,data node index# K=1000,M=10000.0-500M=0500M-1000M=1#1000M-1500M=2
再次重启 mycat
➜ bin ./mycat restart
再次测试添加
两条数据 id 分别为 500 万以上和 500 万以下
具体操作如下
➜ ~ mysql -uroot -h 127.0.0.1 -P 8066 -pmysql> show databases;+----------+| DATABASE |+----------+| lane_db |+----------+1 row in set (0.01 sec)mysql> use lane_dbDatabase changedmysql> insert into position(id,name,salary,city) values(1000,'yangqian1000','1000000'Query OK, 1 row affected (0.07 sec)OK!mysql> insert into position(id,name,salary,city) values(5000010,'yangqian500','1000000'Query OK, 1 row affected (0.01 sec)OK!mysql>

主键生成策略
0 表示使用本地文件方式;
1 表示使用数据库方式生成;
2 表示使用本地时间戳方式;
3 表示基于 ZK 与本地配置的分布式 ID 生成器;
4 表示使用 zookeeper 递增方式生成
0. 基于本地文件
修改 conf 下 sequence_conf.properties
#default global sequenceGLOBAL.HISIDS=GLOBAL.MINID=10001GLOBAL.MAXID=20000GLOBAL.CURID=10000# self define sequenceCOMPANY.HISIDS=COMPANY.MINID=1001COMPANY.MAXID=2000COMPANY.CURID=1000POSITION.HISIDS=POSITION.MINID=1001POSITION.MAXID=2000POSITION.CURID=1000
修改下
<property name="sequenceHandlerType">0</property> <!--0配置文件指定生成,1数据库生成-->
测试下
不写 id
mysql> insert into position(name,salary,city) values('yangqian724','1000000','olmpic');mysql> select * from position;+--------------------+--------------+---------+-----------+| Id | name | salary | city |+--------------------+--------------+---------+-----------+| 2 | yangqian2 | 1000000 | olmpic || 1000 | yangqian1000 | 1000000 | olmpic || 1001 | yangqian724 | 1000000 | olmpic |
可以看到的确是按照我们配置的 position 最小值 1001
指定 id 为 global 配置的生成
mysql> insert into position(id,name,salary,city) values('next value for MYCATSEQ_GLOBAL','yangqian724champion','1000000','olmpic');mysql> select * from position;+--------------------+---------------------+---------+-----------+| Id | name | salary | city |+--------------------+---------------------+---------+-----------+| 2 | yangqian2 | 1000000 | olmpic || 1000 | yangqian1000 | 1000000 | olmpic || 1001 | yangqian724 | 1000000 | olmpic || 10001 | yangqian724champion | 1000000 | olmpic |
可以看到 id 变成了 10001
1. 基于数据库生成
首先指定 database
打开 sequence_db_conf.properties
添加修改内容
#sequence stored in datanodeGLOBAL=dn1COMPANY=dn1CUSTOMER=dn1ORDERS=dn1POSITION=dn1
打开初始化 dbseq.sql
在指定的 lane1 库中执行
DROP TABLE IF EXISTS MYCAT_SEQUENCE;CREATE TABLE MYCAT_SEQUENCE ( name VARCHAR(64) NOT NULL, current_value BIGINT(20) NOT NULL, increment INT NOT NULL DEFAULT 1, PRIMARY KEY (name) ) ENGINE=InnoDB;-- ------------------------------ Function structure for `mycat_seq_currval`-- ----------------------------DROP FUNCTION IF EXISTS `mycat_seq_currval`;DELIMITER ;;CREATE FUNCTION `mycat_seq_currval`(seq_name VARCHAR(64)) RETURNS varchar(64) CHARSET latin1DETERMINISTICBEGINDECLARE retval VARCHAR(64);SET retval="-1,0";SELECT concat(CAST(current_value AS CHAR),",",CAST(increment AS CHAR) ) INTO retval FROM MYCAT_SEQUENCE WHERE name = seq_name;RETURN retval ;END;;DELIMITER ;-- ------------------------------ Function structure for `mycat_seq_nextval`-- ----------------------------DROP FUNCTION IF EXISTS `mycat_seq_nextval`;DELIMITER ;;CREATE FUNCTION `mycat_seq_nextval`(seq_name VARCHAR(64)) RETURNS varchar(64) CHARSET latin1DETERMINISTICBEGINDECLARE retval VARCHAR(64);DECLARE val BIGINT;DECLARE inc INT;DECLARE seq_lock INT;set val = -1;set inc = 0;SET seq_lock = -1;SELECT GET_LOCK(seq_name, 15) into seq_lock;if seq_lock = 1 thenSELECT current_value + increment, increment INTO val, inc FROM MYCAT_SEQUENCE WHERE name = seq_name for update;if val != -1 thenUPDATE MYCAT_SEQUENCE SET current_value = val WHERE name = seq_name;end if;SELECT RELEASE_LOCK(seq_name) into seq_lock;end if;SELECT concat(CAST((val - inc + 1) as CHAR),",",CAST(inc as CHAR)) INTO retval;RETURN retval;END;;DELIMITER ;-- ------------------------------ Function structure for `mycat_seq_setvals`-- ----------------------------DROP FUNCTION IF EXISTS `mycat_seq_nextvals`;DELIMITER ;;CREATE FUNCTION `mycat_seq_nextvals`(seq_name VARCHAR(64), count INT) RETURNS VARCHAR(64) CHARSET latin1DETERMINISTICBEGINDECLARE retval VARCHAR(64);DECLARE val BIGINT;DECLARE seq_lock INT;SET val = -1;SET seq_lock = -1;SELECT GET_LOCK(seq_name, 15) into seq_lock;if seq_lock = 1 thenSELECT current_value + count INTO val FROM MYCAT_SEQUENCE WHERE name = seq_name for update;IF val != -1 THENUPDATE MYCAT_SEQUENCE SET current_value = val WHERE name = seq_name;END IF;SELECT RELEASE_LOCK(seq_name) into seq_lock;end if;SELECT CONCAT(CAST((val - count + 1) as CHAR), ",", CAST(val as CHAR)) INTO retval;RETURN retval;END;;DELIMITER ;-- ------------------------------ Function structure for `mycat_seq_setval`-- ----------------------------DROP FUNCTION IF EXISTS `mycat_seq_setval`;DELIMITER ;;CREATE FUNCTION `mycat_seq_setval`(seq_name VARCHAR(64), value BIGINT) RETURNS varchar(64) CHARSET latin1DETERMINISTICBEGINDECLARE retval VARCHAR(64);DECLARE inc INT;SET inc = 0;SELECT increment INTO inc FROM MYCAT_SEQUENCE WHERE name = seq_name;UPDATE MYCAT_SEQUENCE SET current_value = value WHERE name = seq_name;SELECT concat(CAST(value as CHAR),",",CAST(inc as CHAR)) INTO retval;RETURN retval;END;;DELIMITER ;INSERT INTO MYCAT_SEQUENCE VALUES ('GLOBAL', 1, 1);-- 自己添加的内容INSERT INTO MYCAT_SEQUENCE VALUES ('POSITION', 21, 10);

修改下 server.xml 数据库主键生成策略为 1
<property name="sequenceHandlerType">1</property> <!--0配置文件指定生成,1数据库生成-->
重启 mycat
➜ bin ./mycat restart
执行操作
insert into position(name,salary,city) values('gold','1000000','olmpic');mysql> select * from position;+--------------------+---------------------+---------+-----------+| Id | name | salary | city |+--------------------+---------------------+---------+-----------+| 2 | yangqian2 | 1000000 | olmpic || 22 | gold | 1000000 | olmpic |mysql> insert into position(name,salary,city) values('gold2','1000000','olmpic');mysql> select * from position;+--------------------+---------------------+---------+-----------+| Id | name | salary | city |+--------------------+---------------------+---------+-----------+| 2 | yangqian2 | 1000000 | olmpic || 22 | gold | 1000000 | olmpic || 23 | gold2 | 1000000 | olmpic |
因为步进是 10,下次连接再测试
mysql> use lane_dbmysql> insert into position(name,salary,city) values('medal1','1000000','olmpic');Query OK, 1 row affected (0.14 sec)mysql> select * from position;+--------------------+---------------------+---------+-----------+| Id | name | salary | city |+--------------------+---------------------+---------+-----------+| 2 | yangqian2 | 1000000 | olmpic || 22 | gold | 1000000 | olmpic || 32 | medal1 | 1000000 | olmpic |
2. 基于时间戳的方式
修改下 server.xml 数据库主键生成策略为 2
<property name="sequenceHandlerType">2</property> <!--0配置文件指定生成,1数据库生成,2时间戳-->
因为时间戳生成的 id 比较大,500 万都 hold 不住,需要再次改写 schema.xml 分片规则为 rang-long
<table name="position" primaryKey="id" dataNode="dn1,dn2" rule="mod-long" autoIncrement="true" fetchStoreNodeByJdbc="true"></table>
重启下 mycat
➜ bin ./mycat restart
具体测试
mysql> insert into position(name,salary,city) values('medal2','1000000','olmpic');Query OK, 1 row affected (0.02 sec)OK!mysql> select * from position;+---------------------+---------------------+---------+-----------+| Id | name | salary | city |+---------------------+---------------------+---------+-----------+| 2 | yangqian2 | 1000000 | olmpic || 625399491637280769 | sky4 | 100000 | guangzhou || 1418830095157694465 | medal2 | 1000000 | olmpic |+---------------------+---------------------+---------+-----------+38 rows in set (0.01 sec)
可以看到 id 是时间戳的方式,并远远大于 500M
全局表的创建
分别在 lane1 和 lane2 库 创建 city 表
CREATE TABLE `city` (`Id` bigint(11) NOT NULL AUTO_INCREMENT,`name` varchar(256) DEFAULT NULL,`province` varchar(256) DEFAULT NULL,PRIMARY KEY (`Id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
修改下 schema.xml 添加表 city
<table name="position" primaryKey="id" dataNode="dn1,dn2" rule="mod-long" autoIncrement="true" ></table><table name="city" primaryKey="id" dataNode="dn1,dn2" autoIncrement="true" type="global" ></table>
再次重启
➜ bin ./mycat restart
具体测试操作
➜ ~ mysql -uroot -h 127.0.0.1 -P 8066 -pmysql> show databases;+----------+| DATABASE |+----------+| lane_db |+----------+1 row in set (0.00 sec)mysql> use lane_dbDatabase changedmysql> show tables;+-------------------+| Tables in lane_db |+-------------------+| city || position |+-------------------+2 rows in set (0.01 sec)mysql> select * from city;+--------------------+---------+----------+| Id | name | province |+--------------------+---------+----------+| 624572431226372096 | beijing | beijing |+--------------------+---------+----------+1 row in set (0.08 sec)mysql> insert into city(id,name,province) values(2021,'tokyo','japan');Query OK, 1 row affected (0.05 sec)OK!mysql> select * from city;+--------------------+---------+----------+| Id | name | province |+--------------------+---------+----------+| 2021 | tokyo | japan || 624572431226372096 | beijing | beijing |+--------------------+---------+----------+2 rows in set (0.01 sec)mysql>

可以看到两个库 lane1 和 lane2 都有相同的数据
主从分离
准备工作
在远程创建库 lane1,表 city ,插入数据
mysql> CREATE TABLE `city` (-> `Id` bigint(11) NOT NULL AUTO_INCREMENT,-> `name` varchar(256) DEFAULT NULL,-> `province` varchar(256) DEFAULT NULL,-> PRIMARY KEY (`Id`)-> ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;Query OK, 0 rows affected (0.01 sec)mysql> insert into city(id,name,province) values(2021,'tokyo-5','japan-5');Query OK, 1 row affected (0.01 sec)
主库本地 localhost
从库虚拟机 172.16.94.5
balance 参数:
use0 : 所有读操作都发送到当前可用的 writeHost
use1 :所有读操作都随机发送到 readHost 和 stand by writeHost
use2 :所有读操作都随机发送到 writeHost 和 readHost
use3 :所有读操作都随机发送到 writeHost 对应的 readHost 上,但是 writeHost 不负担读压力
writeType 参数:
use0 : 所有写操作都发送到可用的 writeHost
use1 :所有写操作都随机发送到 readHost
use2 :所有写操作都随机发送到 writeHost,readHost2
修改下 schema.xml 文件
readHost 只能放在 writeHost 里面才行,可以 1 个 writeHost 里放多个 readHost,再加一个 writeHost 做备用
我们是对表来进行读写分离的,也可以不写表则是对库进行读写分离
<?xml version="1.0"?><!DOCTYPE mycat:schema SYSTEM "schema.dtd"><mycat:schema xmlns:mycat="http://io.mycat/"><schema name="lane_db" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1"><!-- auto sharding by id (long) --><!--splitTableNames 启用<table name 属性使用逗号分割配置多个表,即多个表使用这个配置--><!--fetchStoreNodeByJdbc 启用ER表使用JDBC方式获取DataNode--><table name="position" primaryKey="id" dataNode="dn1,dn2" rule="mod-long" autoIncrement="true"></table><table name="city" primaryKey="id" dataNode="dn3" ruleRequired="false"autoIncrement="true"></table><!-- <table name="city" primaryKey="id" dataNode="dn1,dn2" type="global"autoIncrement="true"></table> --></schema><!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"/> --><dataNode name="dn1" dataHost="localhost1" database="lane1" /><dataNode name="dn2" dataHost="localhost1" database="lane2" /><dataNode name="dn3" dataHost="localhost2" database="lane1" /><dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"><heartbeat>select user()</heartbeat><!-- can have multi write hosts --><writeHost host="hostM1" url="jdbc:mysql://localhost:3306" user="root"password="root"></writeHost></dataHost><dataHost name="localhost2" maxCon="1000" minCon="10" balance="1"writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"><heartbeat>select user()</heartbeat><!-- can have multi write hosts --><writeHost host="hostM1" url="jdbc:mysql://localhost:3306" user="root"password="root"><readHost host="hostS1" url="jdbc:mysql://172.16.94.5:3306" user="root"password="root"></readHost></writeHost></dataHost></mycat:schema>


重启 mycat
➜ bin ./mycat restart
再次测试下,可以看到写入的库和读取的库数据的不同 2
➜ ~ mysql -uroot -h 127.0.0.1 -P 8066 -pmysql> select * from city;+------+---------+----------+| Id | name | province |+------+---------+----------+| 2021 | tokyo-5 | japan-5 |+------+---------+----------+1 row in set (0.11 sec)mysql> insert into city(id,name,province) values(2022,'beijing','beijing');Query OK, 1 row affected (0.03 sec)mysql> select * from city;+------+---------+----------+| Id | name | province |+------+---------+----------+| 2021 | tokyo-5 | japan-5 |+------+---------+----------+1 row in set (0.01 sec)

主从分离高可用
如果 readHost 在 writeHost 内部,当出现 外层 writeHost 异常的时候完全不可用,此时需要修改下
修改下 schema.xml 文件
<?xml version="1.0"?><!DOCTYPE mycat:schema SYSTEM "schema.dtd"><mycat:schema xmlns:mycat="http://io.mycat/"><schema name="lane_db" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1"><!-- auto sharding by id (long) --><!--splitTableNames 启用<table name 属性使用逗号分割配置多个表,即多个表使用这个配置--><!--fetchStoreNodeByJdbc 启用ER表使用JDBC方式获取DataNode--><table name="position" primaryKey="id" dataNode="dn1,dn2" rule="mod-long" autoIncrement="true"></table><table name="city" primaryKey="id" dataNode="dn3" ruleRequired="false"autoIncrement="true"></table><!-- <table name="city" primaryKey="id" dataNode="dn1,dn2" type="global"autoIncrement="true"></table> --></schema><!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"/> --><dataNode name="dn1" dataHost="localhost1" database="lane1" /><dataNode name="dn2" dataHost="localhost1" database="lane2" /><dataNode name="dn3" dataHost="localhost2" database="lane1" /><dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"><heartbeat>select user()</heartbeat><!-- can have multi write hosts --><writeHost host="hostM1" url="jdbc:mysql://localhost:3306" user="root"password="root"></writeHost></dataHost><dataHost name="localhost2" maxCon="1000" minCon="10" balance="1"writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"><heartbeat>select user()</heartbeat><!-- can have multi write hosts --><writeHost host="hostM1" url="jdbc:mysql://localhost:3306" user="root"password="root"></writeHost><writeHost host="hostS1" url="jdbc:mysql://172.16.94.5:3306" user="root"password="root"></writeHost></dataHost></mycat:schema>
关掉主库
➜ bin sudo mysql.server stop;Password:Shutting down MySQL.... SUCCESS!➜ bin
再次测试,从库依然可以使用
➜ ~ mysql -uroot -h 127.0.0.1 -P 8066 -pmysql> use lane_db;Database changedmysql> show tables;+-------------------+| Tables in lane_db |+-------------------+| city || position |+-------------------+2 rows in set (0.00 sec)mysql> select * from city;+------+---------+----------+| Id | name | province |+------+---------+----------+| 2021 | tokyo-5 | japan-5 |+------+---------+----------+1 row in set (0.11 sec)mysql> insert into city(id,name,province) values(2024,'chongqing','chongqing');Query OK, 1 row affected (0.01 sec)mysql> select * from city;+------+-----------+-----------+| Id | name | province |+------+-----------+-----------+| 2021 | tokyo-5 | japan-5 || 2024 | chongqing | chongqing |+------+-----------+-----------+2 rows in set (0.00 sec)
如果重新开启主库,则原先的主库变成了从库了
➜ ~ mysql -uroot -h 127.0.0.1 -P 8066 -pEnter password:Welcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 3Server version: 5.6.29-mycat-1.6.7.5-release-20210616151418 MyCat Server (OpenCloudDB)Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.Oracle is a registered trademark of Oracle Corporation and/or itsaffiliates. Other names may be trademarks of their respectiveowners.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql> select * from city;+--------------------+---------+----------+| Id | name | province |+--------------------+---------+----------+| 2021 | tokyo | japan || 2022 | beijing | beijing || 2023 | tianjin | tianjing || 624572431226372096 | beijing | beijing |+--------------------+---------+----------+4 rows in set (0.00 sec)mysql> insert into city(id,name,province) values(2025,'shenzhen','shenzhen');Query OK, 1 row affected (0.01 sec)OK!mysql> select * from city;+--------------------+---------+----------+| Id | name | province |+--------------------+---------+----------+| 2021 | tokyo | japan || 2022 | beijing | beijing || 2023 | tianjin | tianjing || 624572431226372096 | beijing | beijing |+--------------------+---------+----------+4 rows in set (0.01 sec)
强制路由
修改下 schema.xml 下的 localhost2
<dataHost name="localhost2" maxCon="1000" minCon="10" balance="1"writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"><heartbeat>select user()</heartbeat><!-- can have multi write hosts --><writeHost host="hostM1" url="jdbc:mysql://localhost:3306" user="root"password="root"><readHost host="hostS1" url="jdbc:mysql://172.16.94.5:3306" user="root"password="root"/></writeHost><writeHost host="hostS2" url="jdbc:mysql://172.16.94.5:3306" user="root"password="root"></writeHost></dataHost>
重启 mycat
bin ./mycat restart
强制路由到主库
/*!mycat:db_type=master*/ select * from city;
强制路由到从库
mysql> /*!mycat:db_type=slave*/ select * from city;
具体操作如下
可以看到分别查询出了主库和从库的信息
➜ ~ mysql -uroot -h 127.0.0.1 -P 8066 -pmysql> /*!mycat:db_type=master*/ select * from city;+--------------------+---------+----------+| Id | name | province |+--------------------+---------+----------+| 2021 | tokyo | japan || 2022 | beijing | beijing || 2023 | tianjin | tianjing || 624572431226372096 | beijing | beijing |+--------------------+---------+----------+4 rows in set (0.09 sec)mysql> /*!mycat:db_type=slave*/ select * from city;+------+-----------+-----------+| Id | name | province |+------+-----------+-----------+| 2021 | tokyo-5 | japan-5 || 2024 | chongqing | chongqing || 2025 | shenzhen | shenzhen |+------+-----------+-----------+3 rows in set (0.00 sec)mysql>
主从延时切换
避免读取到未同步的从库数据,在未同步的时候读取主库数据,同步之后自动切换读取从库数据
switchType 参数:
-1: 表示不自动切换
1 :表示自动切换
2 :基于 MySQL 主从同步状态决定是否切换
3 :基于 MySQL cluster 集群切换机制
对于非集群
修改 schema.xml
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0"dbType="mysql" dbDriver="native" switchType="2" slaveThreshold="100"><heartbeat>show slave status </heartbeat> <!-- can have multi write hosts --><writeHost host="M1" url="localhost:3306" user="root" password="root"></writeHost><writeHost host="S1" url="localhost:3316" user="root"</dataHost>
对于集群
修改 schema.xml 文件
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0"dbType="mysql" dbDriver="native" switchType="3" ><heartbeat> show status like ‘wsrep%’</heartbeat><writeHost host="M1" url="localhost:3306" user="root"password="root"></writeHost><writeHost host="S1"url="localhost:3316"user="root"password="root" ></writeHost> </dataHost>
Mycat 事务使用
支持 xa 弱事务单库内部可以保证事务的完整性,如果跨库事务, 在执行的时候任何分片出错,可以保证所有分片回滚。
具体操作
#XA 事务需要设置手动提交set autocommit=0;#使用该命令开启 XA 事务set xa=on;#执行相应的 SQL 语句部分insert into city(id,name,province) values(200,'chengdu','sichuan');update position set salary='300000' where id<5;#提交或回滚事务commit; rollback;
实际执行,既是出现异常之后,commit 依然数据回滚
mysql> set autocommit=0;Query OK, 0 rows affected (0.01 sec)mysql> set xa=on;Query OK, 0 rows affected (0.00 sec)mysql> insert into city(id,name,province) values(200,'chengdu','sichuan');Query OK, 1 row affected (0.01 sec)OK!mysql> update position set salary='300000' where id<5;Query OK, 2 rows affected (0.04 sec)OK!mysql> insert into city(id,name,province) values(200,'chengdu3','sichuan3');ERROR 1062 (HY000): Duplicate entry '200' for key 'PRIMARY'mysql> commit;-> commit;Query OK, 0 rows affected (0.00 sec)mysql> /*!mycat:db_type=master*/ select * from city;+--------------------+---------+----------+| Id | name | province |+--------------------+---------+----------+| 2021 | tokyo | japan || 2022 | beijing | beijing || 2023 | tianjin | tianjing || 624572431226372096 | beijing | beijing |+--------------------+---------+----------+4 rows in set (0.01 sec)mysql> /*!mycat:db_type=slave*/ select * from city;+------+-----------+-----------+| Id | name | province |+------+-----------+-----------+| 2021 | tokyo-5 | japan-5 || 2024 | chongqing | chongqing || 2025 | shenzhen | shenzhen |+------+-----------+-----------+3 rows in set (0.01 sec)mysql>
