Mycat学习笔记

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):最终一致是指经过一段时间后,所有节点数据都将会达到一致。

分布式事务几种常见的解决方案:

  1. 全局事务(比如XA两阶段提交;应用、事务管理器(TM)、资源管理器(DB )),例如Atomikos;

  2. 基于可靠消息服务的分布式事务;

  3. 柔性事务TCC(Try-COnfirm-Cancel)tcc-transaction

  4. 最大努力通知,通过消息中间件向其他系统发送消息(重复投递+定期校对)

排序、翻页、函数计算问题
全局主键避重问题
  1. UUID

  2. 数据库

  3. Redis

  4. 雪花算法Snowflake

多数据源/读写数据源的解决方案

分析一下 SQL 执行经过的流程。
DAO——Mapper(ORM)——JDBC——代理——数据库服务

Mycat的概念与配置

Mycat属于上面的代理层

历史:从阿里 cobar 升级而来,由开源组织维护 ,2.0 版本已发布。

定位: 运行在应用和数据库之间, 可以当做一个 MySQL 服务器使用, 实现对 MySQL数据库的分库分表,也可以通过 JDBC 支持其他的数据库。

Mycat 的关键特性(官网首页)http://www.mycat.org.cn/

  1. 可以当做一个 MySQL 数据库来使用;
  2. 支持 MySQL 之外的数据库,通过 JDBC 实现;
  3. 解决了我们提到的所有问题,多表 join、分布式事务、全局序列号、翻页排序;
  4. 支持 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 上,通过远程连接来同步数据,这个是异步的过程。

主从复制的形式
  1. 一主一从/一主多从
  2. 多主一从
  3. 双主复制
  4. 级联复制
主从复制的用途
  1. 数据备份:把数据复制到不同的机器上,以免单台服务器发生故障时数据丢失。
  2. 读写分离:让主库负责写,从库负责读,从而提高读写的并发度。
  3. 高可用HA:当节点故障时,自动转移到其他节点,提高可用性。
  4. 扩展:结合负载的机制,均摊所有的应用访问请求,降低单机 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)

主从复制原理
主从复制配置
  1. 主库开启binlog,设置server-id
  2. 在主库创建具有复制权限的用户,允许从库连接
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'192.168.8.147' IDENTIFIED BY '123456';
FLUSH PRIVILEGES;
  1. 从库/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,实现双主和级联的关键。

  1. 在从库执行
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;
  1. 查看同步状态
SHOW SLAVE STATUS \G
主从复制原理
  1. slave 服务器执行 start slave,开启主从复制开关, slave 服务器的 IO 线程请求从 master 服务器读取 binlog(如果该线程追赶上了主库,会进入睡眠状态)。
  2. master 服务器创建 Log Dump 线程,把 binlog 发送给 slave 服务器。slave 服务器把读取到的 binlog 日志内容写入中继日志 relay log(会记录位置信息,以便下次继续读取)。
  3. 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 可以看到上一层的调用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值