MyCat 概念与配置
1.1 Mycat 介绍与核心概念
- 1.1.1 基本介绍
历史:从阿里 cobar 升级而来,由开源组织维护,2.0 正在开发中。
定位:运行在应用和数据库之间,可以当做一个 MySQL 服务器使用,实现对 MySQL数据库的分库分表,也可以通过 JDBC 支持其他的数据库。

Mycat 的关键特性(官网首页)
- 1、可以当做一个 MySQL 数据库来使用;
- 2、支持 MySQL 之外的数据库,通过 JDBC 实现;
- 3、解决了我们提到的所有问题,多表 join、分布式事务、全局序列号、翻页排序;
- 4、支持 ZK 配置,带监控 mycat-web;
- 5、2.0 正在开发中;
- 1.1.2 核心概念
概念 | 含义 |
主机
|
物理主机,一台服务器,一个数据库服务,一个 3306 端口
|
物理数据库
|
真实的数据库,例如 146、150、151 的 gpcat 数据库
|
物理表
|
真实的表,例如 146、150、151 的 gpcat 数据库的 order_info 表
|
分片
|
将原来单个数据库的数据切分后分散存储在不同的数据库节点
|
分片节点
|
分片以后数据存储的节点
|
分片键
|
分片依据的字段,例如 order_info 表以 id 为依据分片,id 就是分片键,通常是主键
|
分片算法
|
分片的规则,例如随机、取模、范围、哈希、枚举以及各种组合算法
|
逻辑表
|
相对于物理表,是分片表聚合后的结果,对于客户端来说跟真实的表没有区别
|
逻辑数据库
|
相对于物理数据库,是数据节点聚合后的结果,例如 catmall
|
下载、解压 Mycat(有 Windows 版本,可以在本地数据库测试):
http://dl.mycat.io/
1 | wget http: //dl.mycat.io/1.6.7.3/20190927161129/Mycat-server-1.6.7.3-release-20190927161129-linux.tar.gz tar -xzvf Mycat-server-1.6.7.3-release-20190927161129-linux.tar.gz |
Mycat 解压以后有 5 个目录:
目录 | 作用 |
bin
|
启动目录
|
catlet
|
空目录
|
conf
|
配置目录
|
lib
|
jar 包依赖
|
logs
|
日志目录
|
1.2 Mycat 配置详解
主要的配置文件 server.xml、schema.xml、rule.xml 和具体的分片配置文件。
坑非常多,配置错误会导致无法启动,这个时候要看日志!
注意备份,不知道什么时候就跑不起来了……
- 1.2.1 server.xml
包含系统配置信息。
system 标签:例如字符集、线程数、心跳、分布式事务开关等等。
user 标签:配置登录用户和权限。
1 2 3 4 | <user name= "root" defaultAccount= "true" > <property name= "password" > 123456 </property> <property name= "schemas" >catmall</property> </user> |
mycat 对密码加密:
1 | java -cp Mycat-server- 1.6 . 7.3 -release.jar io.mycat.util.DecryptUtil 0 :root: 123456 |
- 1.2.2 schema.xml
https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_schema
schema 在 MySQL 里面跟数据库是等价的。
schema.xml 包括逻辑库、表、分片规则、分片节点和数据源,可以定义多个 schema。
这里面有三个主要的标签(table、dataNode、dataHost):
<table/>
表名和库名最好都用小写
定义了逻辑表,以及逻辑表分布的节点和分片规则:
1 2 3 4 5 6 7 8 9 10 11 | <schema name= "catmall" checkSQLschema= "false" sqlMaxLimit= "100" > <!-- 范围分片 --> <table name= "customer" primaryKey= "id" dataNode= "dn1,dn2,dn3" rule= "rang-long-cust" /> <!-- 取模分片 --> <table name= "order_info" dataNode= "dn1,dn2,dn3" rule= "mod-long-order" > <!-- ER 表 --> <childTable name= "order_detail" primaryKey= "id" joinKey= "order_id" parentKey= "order_id" /> </table> <!-- 全局表 --> <table name= "student" primaryKey= "sid" type= "global" dataNode= "dn1,dn2,dn3" /> </schema> |
配置 |
作用
|
primaryKey
|
指定该逻辑表对应真实表的主键。MyCat 会缓存主键(通过 primaryKey 属性配置)与
具体 dataNode 的信息。
当分片规则(rule)使用非主键进行分片时,那么在使用主键进行查询时,MyCat 就
会通过缓存先确定记录在哪个 dataNode 上,然后再在该 dataNode 上执行查询。
如果没有缓存/缓存并没有命中的话,还是会发送语句给所有的 dataNode。
|
dataNode
|
数据分片的节点
|
autoIncrement
|
自增长(全局序列),true 代表主键使用自增长策略
|
type
|
全局表:global。其他:不配置
|
<dataNode/>
1 | <dataNode name= "dn1" dataHost= "host1" database= "gpcat" /> |
数据节点与物理数据库的对应关系。
配置物理主机的信息,readhost 是从属于 writehost 的。
1 2 3 4 5 6 7 8 9 10 11 12 | <dataHost name= "host1" maxCon= "1000" minCon= "10" balance= "0" writeType= "0" dbType= "mysql" dbDriver= "native" switchType= "1" slaveThreshold= "100" > <heartbeat>select user()</heartbeat> <!-- can have multi write hosts --> <writeHost host= "hostM1" url= "localhost:3306" user= "root" password= "123456" > <!-- can have multi read hosts --> <readHost host= "hostS2" url= "192.168.8.146:3306" user= "root" password= "xxx" /> </writeHost> <writeHost host= "hostS1" url= "localhost:3316" user= "root" password= "123456" /> <!-- <writeHost host= "hostM2" url= "localhost:3316" user= "root" password= "123456" /> --> </dataHost> |
balance:负载的配置,决定 select 语句的负载
值 | 作用 |
0 |
不开启读写分离机制,所有读操作都发送到当前可用的 writeHost 上。
|
1 |
所有读操作都随机发送到当前的 writeHost 对应的 readHost 和备用的 writeHost
|
2 |
所有的读操作都随机发送到所有的 writeHost,readHost 上
|
3 |
所有的读操作都只发送到 writeHost 的 readHost 上
|
writeType:读写分离的配置,决定 update、delete、insert 语句的负载
值 | 作用 |
0 |
所有写操作都发送到可用的 writeHost 上(默认第一个,第一个挂了以后发到第二个)
|
1 |
所有写操作都随机的发送到 writeHost
|
switchType:主从切换配置
值 | 作用 |
-1 |
表示不自动切换
|
1 |
默认值,表示自动切换
|
2 |
基于 MySQL 主从同步的状态决定是否切换,心跳语句为 show slave status
|
3 |
基于 MySQL galary cluster 的切换机制(适合集群)(1.4.1),心跳语句为 show status like 'wsrep%'。
|
- 1.2.3 rule.xml
定义了分片规则和算法
分片规则:
1 2 3 4 5 6 | <tableRule name= "rang-long-cust" > <rule> <columns>id</columns> <algorithm>func-rang- long -cust</algorithm> </rule> </tableRule> |
分片算法:
1 2 3 | <function name= "func-rang-long-cust" class = "io.mycat.route.function.AutoPartitionByLong" > <property name= "mapFile" >rang- long -cust.txt</property> </function |
分片配置:rang-long-cust.txt
1 2 3 | 10001 - 20000 = 1 0 - 10000 = 0 20001 - 100000 = 2 |
3.2.4 ZK 配置
Mycat 也支持 ZK 配置(用于管理配置和生成全局 ID),执行 bin 目录下init_zk_data.sh,会自动将 zkconf 下的所有配置文件上传到 ZK(先拷贝到这个目录)。
1 2 3 4 5 | cd /usr/local/soft/mycat/conf cp *.txt *.xml *.properties zkconf/ cd /usr/local/soft/mycat/bin ./init_zk_data.sh |
启用 ZK 配置:
mycat/conf/myid.properties
1 2 3 4 5 6 7 8 | loadZk= true zkURL= 127.0 . 0.1 : 2181 clusterId= 010 myid= 01001 clusterSize= 1 clusterNodes=mycat_gp_01 #server booster ; booster install on db same server,will reset all minCon to 2 type=server boosterDataHosts=dataHost1 |
注意如果执行 init_zk_data.sh 脚本报错的话,代表未写入成功,此时不要启用 ZK配置并重启,否则本地文件会被覆盖。
启动时如果 loadzk=true 启动时,会自动从 zk 下载配置文件覆盖本地配置。
在这种情况下如果修改配置,需要先修改 conf 目录的配置,copy 到 zkconf,再行上传。

- 1.2.5 启动停止
进入 mycat/bin 目录(注意要先启动物理数据库):
操作 | 命令 |
启动
|
./mycat start
|
停止
|
./mycat stop
|
重启
|
./mycat restart
|
查看状态
|
./mycat status
|
前台运行
|
./mycat console
|
连接:
1 | mysql -uroot -p123456 -h 192.168 . 8.151 -P8066 catmall |
1.3 Mycat 分片验证
explain 可以用来看路由结果
在三个数据库中建表
1 2 3 4 | CREATE TABLE `customer` ( `id` INT ( 11 ) DEFAULT NULL, `name` VARCHAR ( 255 ) DEFAULT NULL ) ENGINE = INNODB DEFAULT CHARSET = utf8; |
1 2 3 4 5 6 7 8 9 | CREATE TABLE `order_info` ( `order_id` INT ( 11 ) NOT NULL COMMENT '订单 ID' , `uid` INT ( 11 ) DEFAULT NULL COMMENT '用户 ID' , `nums` INT ( 11 ) DEFAULT NULL COMMENT '商品数量' , `state` INT ( 2 ) DEFAULT NULL COMMENT '订单状态' , `create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间' , `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' , PRIMARY KEY (`order_id`) ) ENGINE = INNODB DEFAULT CHARSET = utf8; |
1 2 3 4 5 6 7 8 9 10 | CREATE TABLE `order_detail` ( `order_id` INT ( 11 ) NOT NULL COMMENT '订单号' , `id` INT ( 11 ) NOT NULL COMMENT '订单详情' , `goods_id` INT ( 11 ) DEFAULT NULL COMMENT '货品 ID' , `price` DECIMAL ( 10 , 2 ) DEFAULT NULL COMMENT '价格' , `is_pay` INT ( 2 ) DEFAULT NULL COMMENT '支付状态' , `is_ship` INT ( 2 ) DEFAULT NULL COMMENT '是否发货' , `status` INT ( 2 ) DEFAULT NULL COMMENT '订单详情状态' , PRIMARY KEY (`order_id`, `id`) ) ENGINE = INNODB DEFAULT CHARSET = utf8; |
1 2 3 4 5 6 | CREATE TABLE `student` ( `sid` INT ( 8 ) NOT NULL AUTO_INCREMENT, `name` VARCHAR ( 255 ) DEFAULT NULL, `qq` VARCHAR ( 255 ) DEFAULT NULL, PRIMARY KEY (`sid`) ) ENGINE = INNODB DEFAULT CHARSET = utf8; |
schema.xml
1 2 3 4 5 6 7 8 | <table name= "customer" dataNode= "dn1,dn2,dn3" rule= "rang-long-cust" primaryKey= "id" /> <table name= "order_info" dataNode= "dn1,dn2,dn3" rule= "mod-long-order" > <childTable name= "order_detail" joinKey= "order_id" parentKey= "order_id" primaryKey= "id" /> </table> <table name= "student" dataNode= "dn1,dn2,dn3" primaryKey= "sid" type= "global" /> |
数据节点配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <dataNode name= "dn1" dataHost= "host1" database= "gpcat" /> <dataNode name= "dn2" dataHost= "host2" database= "gpcat" /> <dataNode name= "dn3" dataHost= "host3" database= "gpcat" /> <dataHost balance= "0" maxCon= "1000" minCon= "10" name= "host1" writeType= "0" switchType= "1" slaveThreshold= "100" dbType= "mysql" dbDriver= "native" > <heartbeat>select user()</heartbeat> <writeHost host= "hostM1" url= "192.168.8.146:3306" password= "123456" user= "root" /> </dataHost> <dataHost balance= "0" maxCon= "1000" minCon= "10" name= "host2" writeType= "0" switchType= "1" slaveThreshold= "100" dbType= "mysql" dbDriver= "native" > <heartbeat>select user()</heartbeat> <writeHost host= "hostM1" url= "192.168.8.150:3306" password= "123456" user= "root" /> </dataHost> <dataHost balance= "0" maxCon= "1000" minCon= "10" name= "host3" writeType= "0" switchType= "1" slaveThreshold= "100" dbType= "mysql" dbDriver= "native" > <heartbeat>select user()</heartbeat> <writeHost host= "hostM1" url= "192.168.8.151:3306" password= "123456" user= "root" /> </dataHost> |
schema——rule.xml——分片配置。
- 1.3.1 范围分片
1 2 3 4 5 6 | <tableRule name= "rang-long-cust" > <rule> <columns>id</columns> <algorithm>rang- long -cust</algorithm> </rule> </tableRule> |
1 2 3 | <function name= "rang-long-cust" class = "io.mycat.route.function.AutoPartitionByLong" > <property name= "mapFile" >rang- long -cust.txt</property> </function> |
customer:
1 2 3 4 5 6 | INSERT INTO `customer` (`id`, `name`) VALUES ( 6666 , '赵先生' ); INSERT INTO `customer` (`id`, `name`) VALUES ( 7777 , '钱先生' ); INSERT INTO `customer` (`id`, `name`) VALUES ( 16666 , '孙先生' ); INSERT INTO `customer` (`id`, `name`) VALUES ( 17777 , '李先生' ); INSERT INTO `customer` (`id`, `name`) VALUES ( 26666 , '周先生' ); INSERT INTO `customer` (`id`, `name`) VALUES ( 27777 , '吴先生' ); |
- 1.3.2 取模分片(ER 表)
order_info:
1 2 3 4 5 6 | <tableRule name= "mod-long-order" > <rule> <columns>order_id</columns> <algorithm>mod- long </algorithm> </rule> </tableRule> |
1 2 3 | <function name= "mod-long" class = "io.mycat.route.function.PartitionByMod" > <property name= "count" > 3 </property> </function> |
1 2 3 4 5 6 | INSERT INTO `order_info` (`order_id`, `uid`, `nums`, `state`,`create_time`, `update_time`) VALUES ( 1 , 1000001 , 1 , 2 , '2019-9-23 14:35:37' , '2019-9-23 14:35:37' ); INSERT INTO `order_info` (`order_id`,`uid`, `nums`, `state`, `create_time`, `update_time`) VALUES ( 2 , 1000002 , 1 , 2 , '2019-9-24 14:35:37' , '2019-9-24 14:35:37' ); INSERT INTO `order_info` (`order_id`, `uid`, `nums`, `state`, `create_time`,`update_time`) VALUES ( 3 , 1000003 , 3 , 1 , '2019-9-25 11:35:49' , '2019-9-25 11:35:49' ); |
order_detail:
1 2 3 4 5 6 7 8 9 10 11 12 | INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`, `price`,`is_pay`, `is_ship`, `status`) VALUES ( 3 , 20180001 , 85114752 , 19.99 , 1 , 1 , 1 ); INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`,`price`, `is_pay`, `is_ship`, `status`) VALUES ( 1 , 20180002 , 25411251 , 1280.00 , 1 , 1 , 0 ); INSERT INTO `order_detail` (`order_id`, `id`,`goods_id`, `price`, `is_pay`, `is_ship`, `status`) VALUES ( 1 , 20180003 , 62145412 , 288.00 , 1 , 1 , 2 ); INSERT INTO `order_detail` (`order_id`,`id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) VALUES ( 2 , 20180004 , 21456985 , 399.00 , 1 , 1 , 2 ); INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) VALUES ( 2 , 20180005 , 21457452 , 1680.00 , 1 , 1 , 2 ); INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`, `price`, `is_pay`,`is_ship`, `status`) VALUES ( 2 , 20180006 , 65214789 , 9999.00 , 1 , 1 , 3 ); |
- 1.3.3 全局表
student:
1 | <table name= "student" dataNode= "dn1,dn2,dn3" primaryKey= "sid" type= "global" /> |
1 2 3 4 5 6 | INSERT INTO `student` (`sid`, `name`, `qq`) VALUES ( 1 , '黑白' , '166669999' ); INSERT INTO `student` (`sid`, `name`, `qq`) VALUES ( 2 , 'AV哥' , '466669999' ); INSERT INTO `student` (`sid`, `name`, `qq`) VALUES ( 3 , '最强菜鸟' , '368828888' ); INSERT INTO `student` (`sid`, `name`, `qq`) VALUES ( 4 , '加载中' , '655556666' ); INSERT INTO `student` (`sid`, `name`, `qq`) VALUES ( 5 , '猫老公' , '265286999' ); INSERT INTO `student` (`sid`, `name`, `qq`) VALUES ( 6 , '一个人的精彩' , '516895555' ); |
1.4 Mycat 全局 ID
Mycat 全局序列实现方式主要有 4 种:本地文件方式、数据库方式、本地时间戳算法、ZK。也可以自定义业务序列。
注意获取全局 ID 的前缀都是:MYCATSEQ_
- 1.4.1 文件方式
配置文件 server.xml sequnceHandlerType 值:
0 文件 1 数据库 2 本地时间戳 3 ZK
1 | <property name= "sequnceHandlerType" > 0 </property> |
文件方式,配置 conf/sequence_conf.properties:
1 2 3 4 | CUSTOMER.HISIDS= CUSTOMER.MINID= 10000001 CUSTOMER.MAXID= 20000000 CUSTOMER.CURID= 10000001 |
语法:select next value for MYCATSEQ_CUSTOMER
1 | INSERT INTO `customer` (`id`, `name`) VALUES (next value for MYCATSEQ_CUSTOMER, 'qingshan' ); |
优点:本地加载,读取速度较快。
缺点:当 Mycat 重新发布后,配置文件中的 sequence 需要替换。Mycat 不能做集群部署。
- 1.4.2 数据库方式
1 | <property name= "sequnceHandlerType" > 1 </property> |
配置: sequence_db_conf.properties
把这张表创建在 146 上,所以是 dn1
1 2 3 | #sequence stored in datanode GLOBAL=dn1 CUSTOMER=dn1 |
在第一个数据库节点上创建 MYCAT_SEQUENCE 表:
1 2 3 4 5 6 7 8 9 10 | 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 1 , remark VARCHAR ( 100 ), PRIMARY KEY (NAME) ) ENGINE = INNODB; |
注:可以在 schema.xml 配置文件中配置这张表,供外部访问。
1 | <table name= "mycat_sequence" dataNode= "dn1" autoIncrement= "true" primaryKey= "id" ></table> |
创建存储过程——获取当前 sequence 的值
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 30 31 32 33 34 | DROP FUNCTION IF EXISTS `mycat_seq_currval`; DELIMITER; ; CREATE DEFINER = `root`@`%` FUNCTION `mycat_seq_currval` (seq_name VARCHAR( 50 )) RETURNS VARCHAR ( 64 ) CHARSET latin1 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | DROP FUNCTION IF EXISTS `mycat_seq_nextval`; DELIMITER; ; CREATE DEFINER = `root`@`%` FUNCTION `mycat_seq_nextval` (seq_name VARCHAR( 50 )) RETURNS VARCHAR ( 64 ) CHARSET latin1 DETERMINISTIC BEGIN UPDATE MYCAT_SEQUENCE SET current_value = current_value + increment WHERE NAME = seq_name; RETURN mycat_seq_currval (seq_name); END; ; DELIMITER; |
创建存储过程,设置 sequence:
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 | DROP FUNCTION IF EXISTS `mycat_seq_setval`; DELIMITER; ; CREATE DEFINER = `root`@`%` FUNCTION `mycat_seq_setval` ( seq_name VARCHAR ( 50 ), VALUE INTEGER ) RETURNS VARCHAR ( 64 ) CHARSET latin1 DETERMINISTIC BEGIN UPDATE MYCAT_SEQUENCE SET current_value = VALUE WHERE NAME = seq_name; RETURN mycat_seq_currval (seq_name); END; ; DELIMITER; |
插入记录:
1 2 | INSERT INTO MYCAT_SEQUENCE(name,current_value,increment,remark) VALUES ( 'GLOBAL' , 1 , 100 , '' ); INSERT INTO MYCAT_SEQUENCE(name,current_value,increment,remark) VALUES ( 'ORDERS' , 1 , 100 , '订单表使用' ); |
测试:
1 | select next value for MYCATSEQ_ORDERS |
- 1.4.3 本地时间戳方式
ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加) ,长度为18 位
1 | <property name= "sequnceHandlerType" > 2 </property> |
配置文件 sequence_time_conf.properties:
1 2 3 | #sequence depend on TIME WORKID= 01 DATAACENTERID= 01 |
验证:select next value for MYCATSEQ_GLOBAL

- 1.4.4 ZK 方式
修改 conf/myid.properties
设置 loadZk=true(启动时会从 ZK 加载配置,一定要注意备份配置文件,并且先用 bin/init_zk_data.sh,把配置文件写入到 ZK)
1 | <property name= "sequnceHandlerType" > 3 </property> |
配置文件:sequence_distributed_conf.properties:
1 2 3 4 | # 代表使用 zk INSTANCEID=ZK # 与 myid.properties 中的 CLUSTERID 设置的值相同 CLUSTERID= 010 |
复制配置文件
1 2 3 4 5 | cd /usr/local/soft/mycat/conf cp *.txt *.xml *.properties zkconf/ chown -R zkconf/ cd /usr/local/soft/mycat/bin ./init_zk_data.sh |
验证:select next value for MYCATSEQ_GLOBAL

- 1.4.5 使用
在 schema.xml 的 table 标签上配置 autoIncrement="true",不需要获取和指定序列的情况下,就可以使用全局 ID 了。
分类:
MyCat
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具