Mycat学习笔记
Mycat基础
基于Mycat 1.6.7.3版本
数据库优化方案对比
SQL与索引
优化应用端的sql,目的是用到索引
表与存储引擎
数据是存放在表里面的,表又是以不同的格式存放在存储引擎中,所以我们可以选用特定的存储引擎,或对表进行分区,对表结构进行拆分或者冗余处理,或对表结构比如字段的定义进行优化。
架构
如只有一台数据库的服务器,则可以运行多个实例,做集群的方案,负载均衡等。
或者基于主从复制实现读写分离,让写的服务都访问master服务器,读的请求都访问从服务器,slave服务器自动master主服务器同步数据。
或者在数据库前面加一层缓存,减少数据库压力,提升访问速度的目的。
为了分散数据库服务的存储压力和访问压力,我们也可以把不同的数据分不到不同的服务节点,这个就是分库分表(scale out)。
主从(replicate)和分片(shard)的区别:
主从通过数据冗余实现高可用,和实现读写分离。
分片通过拆分数据分散存储和访问压力。
配置
数据库配置的优化,如连接数,缓冲区大小等等。目的使之更高效的利用已有硬件。
操作系统与硬件
从上往下,成本收益比慢慢地在增加。所以肯定不是查询一慢就堆硬件,堆硬件叫做向上的扩展(scale up)。
架构演进与分库分表
单应用单数据库
多应用单数据库
多应用独立数据库
什么时候分表
分表主要是为了减少单张表的大小,解决单表数据量带来的性能问题。
分库分表的类型和特点
从维度来说分为两种:垂直与水平。
垂直切分:基于表或字段划分,表结构不同。有单库的分表,也有多库的分库。
水平切分:基于数据划分,表结构相同,数据不同,也有同库的水平切分和多库的切分。
分库分表带来的问题
跨库关联查询
分布式事务
CAP理论
C(一致性)Consistency:对某个指定的客户端来说,读操作能返回最新的写操作。对于数据分布在不同节点上的数据来说,如果子啊某个节点更新了数据,那么在其他节点如果都能读取到这个而最新的数据,那么就称为强一致,如果有某个节点没有读取到,那就是分布式不一致。
A(可用性)Availability:非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。可用性的两个关键一个是合理的时间,一个是合理的响应。
合理的时间:请求不能无限被阻塞,应该在合理的时间给出返回。
合理的响应:系统应该明确返回结果并且结果是正确的。
P(分区容错性)Partition tolernace:当出现网络分区后,系统能够继续工作。如集群中多台机器,有台机器网络出现了问题,但是这个集群依然可以正常工作。
CAP三者是不能共有的,只能同时满足其中两点。基于AP,我们又有了BASE理论。基本可用(Basically Available):分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。
软状态(Soft state):允许系统中存在中间状态,这个状态不影响系统可用性,这里指的是CAP中的不一致。
最终一致(Eventually consistent):最终一致是指经过一段时间后,所有节点数据都将会达到一致。
分布式事务几种常见的解决方案:
-
全局事务(比如XA两阶段提交;应用、事务管理器(TM)、资源管理器(DB )),例如Atomikos;
-
基于可靠消息服务的分布式事务;
-
柔性事务TCC(Try-COnfirm-Cancel)tcc-transaction
-
最大努力通知,通过消息中间件向其他系统发送消息(重复投递+定期校对)
排序、翻页、函数计算问题
全局主键避重问题
-
UUID
-
数据库
-
Redis
-
雪花算法Snowflake
多数据源/读写数据源的解决方案
分析一下 SQL 执行经过的流程。
DAO——Mapper(ORM)——JDBC——代理——数据库服务
Mycat的概念与配置
Mycat属于上面的代理层
历史:从阿里 cobar 升级而来,由开源组织维护 ,2.0 版本已发布。
定位: 运行在应用和数据库之间, 可以当做一个 MySQL 服务器使用, 实现对 MySQL数据库的分库分表,也可以通过 JDBC 支持其他的数据库。
Mycat 的关键特性(官网首页)http://www.mycat.org.cn/
- 可以当做一个 MySQL 数据库来使用;
- 支持 MySQL 之外的数据库,通过 JDBC 实现;
- 解决了我们提到的所有问题,多表 join、分布式事务、全局序列号、翻页排序;
- 支持 ZK 配置,带监控 mycat-web。
Mycat配置详解
主要的配置文件 server.xml、schema.xml、rule.xml 和具体的分片配置文件。
server.xml
包含系统配置信息。
system 标签:例如字符集、线程数、心跳、分布式事务开关等等。
user 标签:配置登录用户和权限。
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">catmall</property>
</user>
mycat 对密码加密:
java -cp Mycat-server-1.6.7.3-release.jar io.mycat.util.DecryptUtil 0:root:123456
schema.xml
schema 在 MySQL 里面跟数据库是等价的。
schema.xml 包括逻辑库、 表、 分片规则、 分片节点和数据源, 可以定义多个 schema。
这里面有三个主要的标签(table、dataNode、dataHost):
table:
表名和库名最好都用小写
定义了逻辑表,以及逻辑表分布的节点和分片规则:
<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:
数据节点与物理数据库的对应关系。
<dataNode name="dn1" dataHost="host1" database="gpcat" />
dataHost:
配置物理主机的信息,readhost 是从属于 writehost 的。
<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%’。 |
rule.xml
定义了分片规则和算法
分片规则:
<tableRule name="rang-long-cust">
<rule>
<columns>id</columns>
<algorithm>func-rang-long-cust</algorithm>
</rule>
</tableRule>
分片算法:
<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
10001-20000=1
0-10000=0
20001-100000=2
ZK配置
参考资料:https://www.cnblogs.com/leeSmall/p/9551038.html
Mycat 也支持 ZK 配置(用于管理配置和生成全局 ID),执行 bin 目录下init_zk_data.sh,会自动将 zkconf 下的所有配置文件上传到 ZK(先拷贝到这个目录)。
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
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,再执行上传。
启动停止
进入 mycat/bin 目录(注意要先启动物理数据库):
操作 | 命令 |
---|---|
启动 | ./mycat start |
停止 | ./mycat stop |
重启 | ./mycat restart |
查看状态 | ./mycat status |
前台运行 | ./mycat console |
连接:
mysql -uroot -p123456 -h 192.168.8.151 -P8066 catmall
Mycat 分片验证
explain 可以用来看路由结果
在三个数据库中建表
CREATE TABLE `customer` (
`id` int(11) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATETABLE`order_info`(
`order_id`int(11)NOTNULLCOMMENT'订单ID',
`uid`int(11)DEFAULTNULLCOMMENT'用户ID',
`nums`int(11)DEFAULTNULLCOMMENT'商品数量',
`state`int(2)DEFAULTNULLCOMMENT'订单状态',
`create_time`datetimeDEFAULTNULLONUPDATECURRENT_TIMESTAMPCOMMENT'创建时间',
`update_time`datetimeDEFAULTNULLONUPDATECURRENT_TIMESTAMPCOMMENT'更新时间',
PRIMARYKEY(`order_id`)
)ENGINE=InnoDBDEFAULTCHARSET=utf8;
CREATETABLE`order_detail`(
`order_id`int(11)NOTNULLCOMMENT'订单号',
`id`int(11)NOTNULLCOMMENT'订单详情',
`goods_id`int(11)DEFAULTNULLCOMMENT'货品ID',
`price`decimal(10,2)DEFAULTNULLCOMMENT'价格',
`is_pay`int(2)DEFAULTNULLCOMMENT'支付状态',
`is_ship`int(2)DEFAULTNULLCOMMENT'是否发货',
`status`int(2)DEFAULTNULLCOMMENT'订单详情状态',
PRIMARYKEY(`order_id`,`id`)
)ENGINE=InnoDBDEFAULTCHARSET=utf8;
CREATETABLE`student`(
`sid`int(8)NOTNULLAUTO_INCREMENT,
`name`varchar(255)DEFAULTNULL,
`qq`varchar(255)DEFAULTNULL,
PRIMARYKEY(`sid`)
)ENGINE=InnoDBDEFAULTCHARSET=utf8;
schema.xml
<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">
<childTablename="order_detail"joinKey="order_id"parentKey="order_id"primaryKey="id"/>
</table>
<table name="student"dataNode="dn1,dn2,dn3"primaryKey="sid"type="global"/>
数据节点配置
<dataNodename="dn1"dataHost="host1"database="gpcat"/>
<dataNodename="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—分片配置
范围分片
<tableRule name="rang-long-cust">
<rule>
<columns>id</columns>
<algorithm>rang-long-cust</algorithm>
</rule>
</tableRule>
<function name="rang-long-cust" class="io.mycat.route.function.AutoPartitionByLong">
<property name="mapFile">rang-long-cust.txt</property>
</function>
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, '吴先生');
取模分片(ER表)
order_info
<tableRule name="mod-long-order">
<rule>
<columns>order_id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<property name="count">3</property>
</function
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');
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)
全局表
student
<table name="student" dataNode="dn1,dn2,dn3" primaryKey="sid" type="global"/>
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (1, '黑白', '166669999');
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (2, 'A', '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');
Mycat全局ID
Mycat全局序列实现方式主要有 4 种:本地文件方式、数据库方式、本地时间戳算法、ZK。
也可以自定义业务序列。
注意获取全局 ID 的前缀都是:MYCATSEQ_
文件方式
配置文件 server.xml sequnceHandlerType 值:
0 文件
1 数据库
2 本地时间戳
3 ZK
<property name="sequnceHandlerType">0</property>
文件方式,配置 conf/sequence_conf.properties
CUSTOMER.HISIDS=
CUSTOMER.MINID=10000001
CUSTOMER.MAXID=20000000
CUSTOMER.CURID=10000001
语法:select next value for MYCATSEQ_CUSTOMER
INSERT INTO `customer` (`id`, `name`) VALUES (next value for MYCATSEQ_CUSTOMER, 'qingshan');
优点:本地加载,读取速度较快。
缺点:当 Mycat 重新发布后,配置文件中的 sequence 需要替换。Mycat 不能做集群部署。
数据库方式
<property name="sequnceHandlerType">1</property>
配置: sequence_db_conf.properties (把这张表创建在 146 上,所以是 dn1)
#sequence stored in datanode
GLOBAL=dn1
CUSTOMER=dn1
在第一个数据库节点上创建 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 1,
remark varchar(100),
PRIMARY KEY(name)) ENGINE=InnoDB;
注:可以在 schema.xml 配置文件中配置这张表,供外部访问。
<table name="mycat_sequence" dataNode="dn1" autoIncrement="true" primaryKey="id"></table>
创建存储过程——获取当前 sequence 的值
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
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
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 ;
插入记录
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,'订单表使用');
测试
select next value for MYCATSEQ_ORDERS
本地时间戳方式
ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加) ,长度为18 位
<property name="sequnceHandlerType">2</property>
配置文件 sequence_time_conf.properties
#sequence depend on TIME
WORKID=01
DATAACENTERID=01
验证
select next value for MYCATSEQ_GLOBAL
ZK方式
修改 conf/myid.properties
设置 loadZk=true(启动时会从 ZK 加载配置,一定要注意备份配置文件,并且先用 bin/init_zk_data.sh,把配置文件写入到 ZK)
<property name="sequnceHandlerType">3</property>
配置文件:sequence_distributed_conf.properties
# 代表使用 zk
INSTANCEID=ZK
# 与 myid.properties 中的 CLUSTERID 设置的值相同
CLUSTERID=010
复制配置文件
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
使用
在 schema.xml 的 table 标签上配置 autoIncrement=“true”,不需要获取和指定序列的情况下,就可以使用全局 ID 了。
Mycat 监控与日志查看
监控
命令行监控
连接到管理端口 9066,注意必须要带 IP
mysql-uroot-h127.0.0.1-p123456-P9066
全部命令:
mysql>show@@help;
命令 | 作用 |
---|---|
show@@server | 查看数据库状态,包括内存等 |
show@@database | 查看数据库 |
show@@datanode | 查看数据节点 |
show@@datasource | 查看数据源 |
show@@connection | 该命令用于获取 Mycat 的前端连接状态, 即 应用与 mycat 的连接 |
show @@backend | 查看后端连接状态 |
show @@cache | 查看缓存使用情况 SQLRouteCache: sql 路由缓存。 TableID2DataNodeCache : 缓存表主键与分 片对应关系。 ER_SQL2PARENTID : 缓存 ER 分片中子表与 父表关系 |
reload @@config | 重新加载基本配置, 使用这个命令时 mycat 服务不可用 |
show @@sysparam | 参看参数 |
show @@sql.high | 执行频率高的 SQL |
show @@sql.slow | 慢 SQL 设置慢 SQL 的命令: reload @@sqlslow=5 ; |
命令行监控 mycatweb 监控
https://github.com/MyCATApache/Mycat-download/tree/master/mycat-web-1.0
Mycat-eye 是 mycat 提供的一个监控工具,它依赖于 ZK。
本地必须要运行一个 ZK,必须先启动 ZK。
参考: https://gper.club/articles/7e7e7f7ff7g59gc3g64
下载 mycat-web
cd /usr/local/soft
wget http://dl.mycat.io/mycat-web-1.0/Mycat-web-1.0-SNAPSHOT-20170102153329-linux.tar.gz
tar -xzvf Mycat-web-1.0-SNAPSHOT-20170102153329-linux.tar.gz
启动 mycat-web
cd mycat-web
nohup ./start.sh &
停止:kill start.jar 相关的进程
访问端口 8082
http://192.168.8.151:8082/mycat/
mycat server.xml 配置
<!-- 1 为开启实时统计、 0 为关闭 -->
<property name="useSqlStat">1</property>
重启 mycat 服务生效
日志
log4j 的 level 配置要改成 debug
wrapper.log 日志
wrapper 日志:mycat 启动,停止,添加为服务等都会记录到此日志文件,如果系统环境配置错误或缺少配置时,导致 Mycat 无法启动,可以通过查看 wrapper.log 定位具体错误原因。
mycat.log 日志
mycat.log 为 mycat 主要日志文件,记录了启动时分配的相关 buffer 信息,数据源连接信息,连接池,动态类加载信息等等。
在 conf/log4j2.xml 文件中进行相关配置,如保留个数,大小,字符集,日志文件大小等。
Mycat进阶
Mysql主从复制
含义:
主从复制,就是把主节点的数据复制到一个或者多个从节点。主服务器和从服务器可以在不同的 IP 上,通过远程连接来同步数据,这个是异步的过程。
主从复制的形式
- 一主一从/一主多从
- 多主一从
- 双主复制
- 级联复制
主从复制的用途
- 数据备份:把数据复制到不同的机器上,以免单台服务器发生故障时数据丢失。
- 读写分离:让主库负责写,从库负责读,从而提高读写的并发度。
- 高可用HA:当节点故障时,自动转移到其他节点,提高可用性。
- 扩展:结合负载的机制,均摊所有的应用访问请求,降低单机 IO。
binlog
客户端对 MySQL 数据库进行操作的时候,包括 DDL 和 DML 语句,服务端会在日志文件中用事件的形式记录所有的操作记录,这个文件就是 binlog 文件(属于逻辑日志,跟 Redis 的 AOF 文件类似)。
基于 binlog,我们可以实现主从复制和数据恢复。
Binlog 默认是不开启的,需要在服务端手动配置。注意有一定的性能损耗。
binlog 配置
编辑 /etc/my.cnf
log-bin=/var/lib/mysql/mysql-bin
server-id=1
重启 MySQL 服务
service mysqld stop
service mysqld start
## 如果出错查看日志
vi /var/log/mysqld.log
cd /var/lib/mysql
是否开启binlog
show variables like 'log_bin%';
binlog 格式
STATEMENT:记录每一条修改数据的 SQL 语句(减少日志量,节约 IO)。
ROW:记录哪条数据被修改了,修改成什么样子了(5.7 以后默认)。
MIXED:结合两种方式,一般的语句用 STATEMENT,函数之类的用 ROW。
查看 binlog 格式:
show global variables like '%binlog_format%';
查看binlog列表
show binary logs;
查看binlog内容
show binlog events in 'mysql-bin.000001';
用 mysqlbinlog 工具,基于时间查看 binlog
(注意这个是 Linux 命令, 不是 SQL)
主从复制原理
主从复制配置
- 主库开启binlog,设置server-id
- 在主库创建具有复制权限的用户,允许从库连接
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'192.168.8.147' IDENTIFIED BY '123456';
FLUSH PRIVILEGES;
- 从库/etc/my.cnf 配置,重启数据库
server-id=2
log-bin=mysql-bin
relay-log=mysql-relay-bin
read-only=1
log-slave-updates=1
log-slave-updates 决定了在从 binlog 读取数据时,是否记录 binlog,实现双主和级联的关键。
- 在从库执行
stop slave;
change master to
master_host='192.168.8.146',master_user='repl',master_password='123456',master_log_file='mysql-bin.000001',
master_log_pos=4;
start slave;
- 查看同步状态
SHOW SLAVE STATUS \G
主从复制原理
- slave 服务器执行 start slave,开启主从复制开关, slave 服务器的 IO 线程请求从 master 服务器读取 binlog(如果该线程追赶上了主库,会进入睡眠状态)。
- master 服务器创建 Log Dump 线程,把 binlog 发送给 slave 服务器。slave 服务器把读取到的 binlog 日志内容写入中继日志 relay log(会记录位置信息,以便下次继续读取)。
- slave 服务器的 SQL 线程会实时检测 relay log 中新增的日志内容,把 relay log解析成 SQL 语句,并执行。
Mycat高可用
目前 Mycat 没有实现对多 Mycat 集群的支持,可以暂时使用 HAProxy 来做负载;
思路:HAProxy 对 Mycat 进行负载。Keepalived 实现 VIP。
Mycat注解
注解的作用
当关联的数据不在同一个节点的时候,Mycat 是无法实现跨库 join 的。
例:如果直接在 150 插入主表数据,151 插入明细表数据,此时关联查询无法查询出来。
Mycat 作为一个中间件,有很多自身不支持的 SQL 语句,比如存储过程,但是这些语句在实际的数据库节点上是可以执行的。 有没有办法让 Mycat 做一层透明的代理转发,直接找到目标数据节点去执行这些 SQL 语句呢?
那我们必须要有一种方式告诉 Mycat 应该在哪个节点上执行。 这个就是 Mycat 的注解。我们在需要执行的 SQL 语句前面加上一段代码,帮助 Mycat 找到我们的目标节点。
注解的用法
注解的形式是 :
/*!mycat: sql=注解 SQL 语句*/
注解的使用方式是 :
/*!mycat: sql=注解 SQL 语句*/ 真正执行的 SQL
使用时将 = 号后的 “注解 SQL 语句” 替换为需要的 SQL 语句即可。
使用注解有一些限制,或者注意的地方:
原始SQL | 注解 SQL |
---|---|
select | 如果需要确定分片, 则使用能确定分片的注解, 比如/!mycat: sql=select * from users where user_id=1/ 如果要在所有分片上执行则可以不加能确定分片的条件 |
insert | 使用 insert 的表作为注解 SQL, 必须能确定到某个分片 原始 SQL 插入的字段必须包括分片字段 非分片表( 只在某个节点上) : 必须能确定到某个分片 |
delete | 使用 delete 的表作为注解 SQL |
update | 使用 update 的表作为注解 SQL |
使用注解并不额外增加 MyCat 的执行时间;从解析复杂度以及性能考虑,注解SQL 应尽量简单,因为它只是用来做路由的。
注解的使用示例
创建表或存储过程
customer.id=1 全部路由到 146
-- 存储过程
/*!mycat: sql=select * from customer where id =1 */ CREATE PROCEDURE test_proc() BEGIN END ;
-- 表
/*!mycat: sql=select * from customer where id =1 */ CREATE TABLE test2(id INT);
特殊语句自定义分片
Mycat 本身不支持 insert select,通过注解支持
/*!mycat: sql=select * from customer where id =1 */ INSERT INTO test2(id) SELECT id FROM order_detail;
多表 ShareJoin
/*!mycat:catlet=io.mycat.catlets.ShareJoin */
select a.order_id,b.price from order_info a, order_detail b where a.nums = b.goods_id;
读写分离
读写分离 : 配置 Mycat 读写分离后,默认查询都会从读节点获取数据,但是有些场景需要获取实时数据,如果从读节点获取数据可能因延时而无法实现实时,Mycat 支持通过注解 /balance/ 来强制从写节点(write host)查询数据。
/*balance*/ select a.* from customer a where a.id=6666;
读写分离数据库选择( 1.6 版本之后)
/*!mycat: db_type=master */ select * from customer;
/*!mycat: db_type=slave */ select * from customer;
/*#mycat: db_type=master */ select * from customer;
/*#mycat: db_type=slave */ select * from customer;
注解支持的’! ‘不被 mysql 单库兼容
注解支持的’#'不被 MyBatis 兼容
随着 Mycat 的开发,更多的新功能正在加入。
注解原理
Mycat 在执行 SQL 之前会先解析 SQL 语句,在获得分片信息后再到对应的物理节点上执行。如果 SQL 语句无法解析,则不能被执行。如果语句中有注解,则会先解析注解的内容获得分片信息,再把真正需要执行的 SQL 语句发送对对应的物理节点上。
分片策略详解
分片的目标是将大量数据和访问请求均匀分布在多个节点上,通过这种方式提升数据服务的存储和负载能力。
Mycat 分片策略详解
总体上分为连续分片和离散分片,还有一种是连续分片和离散分片的结合,例如先范围后取模。
比如范围分片(id 或者时间)就是典型的连续分片,单个分区的数量和边界是确定的。离散分片的分区总数量和边界是确定的,例如对 key 进行哈希运算,或者再取模。
连续分片优点:
1)范围条件查询消耗资源少(不需要汇总数据)
2)扩容无需迁移数据(分片固定)
连续分片缺点:
1)存在数据热点的可能性
2)并发访问能力受限于单一或少量 DataNode(访问集中)
离散分片优点:
1)并发访问能力增强(负载到不同的节点)
2)范围条件查询性能提升(并行计算)
离散分片缺点:
1)数据扩容比较困难,涉及到数据迁移问题
2)数据库连接消耗比较多
连续分片
范围分片
<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>
# range start-end ,data node index
# K=1000,M=10000.
0-500M=0
500M-1000M=1
1000M-1500M=2
特点:容易出现冷热数据
按自然月分片
建表语句
CREATE TABLE `sharding_by_month` (
`create_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
`db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
逻辑表
<schema name="catmall" checkSQLschema="false" sqlMaxLimit="100">
<table name="sharding_by_month" dataNode="dn1,dn2,dn3" rule="sharding-by-month" />
</schema>
分片规则
<tableRule name="sharding-by-month">
<rule>
<columns>create_time</columns>
<algorithm>qs-partbymonth</algorithm>
</rule>
</tableRule>
分片算法
<function name="qs-partbymonth" class="io.mycat.route.function.PartitionByMonth">
<property name="dateFormat">yyyy-MM-dd</property>
<property name="sBeginDate">2019-10-01</property>
<property name="sEndDate">2019-12-31</property>
</function>
columns 标识将要分片的表字段, 字符串类型, 与 dateFormat 格式一致。
algorithm 为分片函数。
dateFormat 为日期字符串格式。
sBeginDate 为开始日期。
sEndDate 为结束日期
注意: 节点个数要大于月份的个数
测试语句
INSERT INTO sharding_by_month (create_time,db_nm) VALUES ('2019-10-16', database());
INSERT INTO sharding_by_month (create_time,db_nm) VALUES ('2019-10-27', database());
INSERT INTO sharding_by_month (create_time,db_nm) VALUES ('2019-11-04', database());
INSERT INTO sharding_by_month (create_time,db_nm) VALUES ('2019-11-11', database());
INSERT INTO sharding_by_month (create_time,db_nm) VALUES ('2019-12-25', database());
INSERT INTO sharding_by_month (create_time,db_nm) VALUES ('2019-12-31', database());
离散分片
枚举分片
将所有可能出现的值列举出来,指定分片。例如:全国 34 个省,要将不同的省的数据存放在不同的节点,可用枚举的方式。
建表语句:
CREATE TABLE `sharding_by_intfile` (
`age` int(11) NOT NULL,
`db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
逻辑表:
<table name="sharding_by_intfile" dataNode="dn$1-3" rule="sharding-by-intfile" />
分片规则:
<tableRule name="sharding-by-intfile">
<rule>
<columns>sharding_id</columns>
<algorithm>hash-int</algorithm>
</rule>
</tableRule>
分片算法:
<function name="hash-int" class="org.opencloudb.route.function.PartitionByFileMap">
<property name="mapFile">partition-hash-int.txt</property>
<property name="type">0</property>
<property name="defaultNode">0</property>
</function>
type:默认值为 0,0 表示 Integer,非零表示 String。
PartitionByFileMap.java,通过 map 来实现。
策略文件:partition-hash-int.txt
16=0
17=1
18=2
插入数据测试:
INSERT INTO `sharding_by_intfile` (age,db_nm) VALUES (16, database());
INSERT INTO `sharding_by_intfile` (age,db_nm) VALUES (17, database());
INSERT INTO `sharding_by_intfile` (age,db_nm) VALUES (18, database());
一致性哈希
一致性Hash有效的解决了分布式数据的扩容的问题
建表语句
CREATE TABLE `sharding_by_murmur` (
`id` int(10) DEFAULT NULL,
`db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
逻辑表
<schema name="test" checkSQLschema="false" sqlMaxLimit="100">
<table name="sharding_by_murmurhash" primaryKey="id" dataNode="dn$1-3" rule="sharding-by-murmur" />
</schema>
分片规则:
<tableRule name="sharding-by-murmur">
<rule>
<columns>id</columns>
<algorithm>qs-murmur</algorithm>
</rule>
</tableRule>
分片算法
<function name="qs-murmur" class="io.mycat.route.function.PartitionByMurmurHash">
<property name="seed">0</property>
<property name="count">3</property>
<property name="virtualBucketTimes">160</property>
</function>
插入测试数据
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (1, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (2, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (3, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (4, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (5, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (6, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (7, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (8, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (9, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (10, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (11, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (12, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (13, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (14, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (15, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (16, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (17, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (18, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (19, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (20, database());
特点:可以一定程度减少数据的迁移。
十进制取模分片
分片规则
<tableRule name="mod-long">
<rule>
<columns>sid</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
分片算法
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">3</property>
</function>
特点:分布均匀,但是迁移工作量比较大
固定分片哈希
这是先求模得到逻辑分片号,再根据逻辑分片号直接映射到物理分片的一种散列算法。
建表语句:
CREATE TABLE `sharding_by_long` (
`id` int(10) DEFAULT NULL,
`db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
逻辑表:
<schema name="test" checkSQLschema="false" sqlMaxLimit="100">
<table name="sharding_by_long" dataNode="dn$1-3" rule="qs-sharding-by-long" />
</schema>
分片规则:
<tableRule name="qs-sharding-by-long">
<rule>
<columns>id</columns>
<algorithm>qs-sharding-by-long</algorithm>
</rule>
</tableRule>
分片算法:平均分成 8 片(%1024 的余数,1024=128*8):
<function name="qs-sharding-by-long" class="io.mycat.route.function.PartitionByLong">
<property name="partitionCount">8</property>
<property name="partitionLength">128</property>
</function>
* partitionCount 为指定分片个数列表。
* partitionLength 为分片长度范围列表。
插入测试数据:
INSERT INTO `sharding_by_long` (id,db_nm) VALUES (222, database());
INSERT INTO `sharding_by_long` (id,db_nm) VALUES (333, database());
INSERT INTO `sharding_by_long` (id,db_nm) VALUES (666, database());
特点:在一定范围内 id 是连续分布的。
取模范围分片
建表语句:
CREATE TABLE `sharding_by_pattern` (
`id` varchar(20) DEFAULT NULL,
`db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
逻辑表:
<schema name="test" checkSQLschema="false" sqlMaxLimit="100">
<table name="sharding_by_pattern" primaryKey="id" dataNode="dn$0-10" rule="qs-sharding-by-pattern" />
</schema>
分片规则:
<tableRule name="sharding-by-pattern">
<rule>
<columns>user_id</columns>
<algorithm>sharding-by-pattern</algorithm>
</rule>
</tableRule>
分片算法:
<function name="sharding-by-pattern" class=" io.mycat.route.function.PartitionByPattern">
<property name="patternValue">100</property>
<property name="defaultNode">0</property>
<property name="mapFile">partition-pattern.txt</property>
</function>
patternValue 取模基数,这里设置成 100
partition-pattern.txt,一共 3 个节点
# id partition range start-end ,data node index
###### first host configuration
1-20=0
21-70=1
71-100=2
0-0=0
插入测试数据
INSERT INTO `sharding_by_pattern` (id,db_nm) VALUES (19, database());
INSERT INTO `sharding_by_pattern` (id,db_nm) VALUES (222, database());
INSERT INTO `sharding_by_pattern` (id,db_nm) VALUES (371, database());
id=19%100=19,在 dn1;
id=222%100=22,dn2;
id=371%100=71,dn3
特点:可以调整节点的数据分布。
范围取模分片
建表语句:
CREATE TABLE `sharding_by_rang_mod` (
`id` bigint(20) DEFAULT NULL,
`db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
逻辑表:
<schema name="test" checkSQLschema="false" sqlMaxLimit="100">
<table name="sharding_by_rang_mod" dataNode="dn$1-3" rule="qs-sharding-by-rang-mod" />
</schema>
分片规则:
<tableRule name="qs-sharding-by-rang-mod">
<rule>
<columns>id</columns>
<algorithm>qs-rang-mod</algorithm>
</rule>
</tableRule>
分片算法:
<function name="qs-rang-mod" class="io.mycat.route.function.PartitionByRangeMod">
<property name="mapFile">partition-range-mod.txt</property>
</function>
partition-range-mod.txt
# range start-end ,data node group size
0-20000=1
20001-40000=2
解读:先范围后取模。Id 在 20000 以内的,全部分布到 dn1。Id 在 20001-40000的,%2 分布到 dn2,dn3。
插入测试数据:
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (666, database());
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (6667, database());
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (16666, database());
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (21111, database());
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (22222, database());
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (23333, database());
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (24444, database());
特点:扩容的时候旧数据无需迁移
其他分片规则
应用指定分片 PartitionDirectBySubString
日期范围哈希 PartitionByRangeDateHash
冷热数据分片 PartitionByHotDate
也可以自定义分片规则 : extends AbstractPartitionAlgorithm implements RuleAlgorithm。
分片规则的选择
步骤:
1、找到需要切分的大表,和关联的表
2、确定分片字段(尽量使用主键),一般用最频繁使用的查询条件
3、考虑单个分片的存储容量和请求、数据增长(业务特性)、扩容和数据迁移问题
例如:按照什么递增?序号还是日期?主键是否有业务意义?
一般来说,分片数要比当前规划的节点数要大。
总结:根据业务场景,合理地选择分片规则。
Mycat离线扩缩容
当我们规划了数据分片,数据已经超过单个节点的存储上限,或者需要下线节点的时候,就需要对数据重新分片。
此处笔记在动手试验后再补充。
核心流程总结
启动
1、MycatServer 启动,解析配置文件,包括服务器、分片规则等
2、创建工作线程,建立前端连接和后端连接
执行SQL
1、前端连接接收 MySQL 命令
2、解析 MySQL,Mycat 用的是 Druid 的 DruidParser
3、获取路由
4、改写 MySQL,例如两个条件在两个节点上,则变成两条单独的 SQL
例如 select * from customer where id in(5000001, 10000001);
改写成:
select * from customer where id = 5000001;(dn2 执行)
select * from customer where id = 10000001;(dn3 执行)
又比如多表关联查询,先到各个分片上去获取结果,然后在内存中计算
5、与后端数据库建立连接
6、发送 SQL 语句到 MySQL 执行
7、获取返回结果
8、处理返回结果,例如排序、计算等等
9、返回给客户端
源码下载与环境调试
下载源码,导入工程
git clone https://github.com/MyCATApache/Mycat-Server
配置schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100">
<table name="travelrecord" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />
<table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3" rule="mod-long" />
</schema>
<dataNode name="dn1" dataHost="localhost1" database="db1" />
<dataNode name="dn2" dataHost="localhost1" database="db2" />
<dataNode name="dn3" dataHost="localhost1" database="db3" />
<dataHost name="localhost1" maxCon="20" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="127.0.0.1:3306" user="root"
password="123456">
</writeHost>
</dataHost>
</mycat:schema>
创建表结构
本地数据库创建 db1、db2、db3 数据库,全部执行建表脚本
CREATE TABLE `company` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(64) DEFAULT '',
`market_value` bigint(20) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
CREATE TABLE `hotnews` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`title` varchar(64) DEFAULT '',
`content` varchar(512) DEFAULT '0',
`time` varchar(8) DEFAULT '',
`cat_name` varchar(10) DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `travelrecord` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`city` varchar(32) DEFAULT '',
`time` varchar(8) DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
逻辑表配置
travelrecord表配置
<table name="travelrecord" dataNode="dn1,dn2,dn3" 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>
hotnews表配置
<table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3" rule="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">3</property>
</function>
company表配置
<table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />
debug方式启动
debug 方式启动 main 方法
Mycat-Server-1.6.5-RELEASE\src\main\java\io\mycat\MycatStartup.java
连接本机 Mycat 服务
插入测试语句
insert into travelrecord(`id`, `city`, `time`) values(1, '长沙', '20191020');
insert into hotnews(`title`, `content`) values('GP', '小青');
insert into company(`name`, `market_value`) values('spring', 100);
调试入口
连接入口
io.mycat.net.NIOAcceptor#accept
SQL入口
io.mycat.server.ServerQueryHandler#query
varchar(32) DEFAULT ‘’,
time
varchar(8) DEFAULT ‘’,
PRIMARY KEY (id
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
##### 逻辑表配置
travelrecord表配置
~~~xml
<table name="travelrecord" dataNode="dn1,dn2,dn3" 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>
hotnews表配置
<table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3" rule="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">3</property>
</function>
company表配置
<table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />
debug方式启动
debug 方式启动 main 方法
Mycat-Server-1.6.5-RELEASE\src\main\java\io\mycat\MycatStartup.java
连接本机 Mycat 服务
插入测试语句
insert into travelrecord(`id`, `city`, `time`) values(1, '长沙', '20191020');
insert into hotnews(`title`, `content`) values('GP', '小青');
insert into company(`name`, `market_value`) values('spring', 100);
调试入口
连接入口
io.mycat.net.NIOAcceptor#accept
SQL入口
io.mycat.server.ServerQueryHandler#query
Step Over 可以看到上一层的调用