用mycat进行了简单的分表练习.我是基于docker的mycat 使用.只做了简单的取模分表.

什么情况下需要分表?

一般是业务量比较大的时候需要分表, 将一个表的数据存储到不同的mysql 服务器上,然后分摊服务器的压力.

如何分表?

分表的难点,不是如何把表拆分, 而是要拆分哪些表, 与其关联的相关表也要做处理. 和被拆分的表关联紧密的表也要做拆分,有些关联不紧密,但是所有节点都需要查询的, 就做全局表,所有节点共存一份.
比如商城的订单表, 拆分的话, 就以用户这个字段为维度进行拆分, 因为一个用户购买 的东西是有限的, 因此作为拆分比较合理. 如果拿时间,或者地点字段做拆分, 也不好做,因为用户的下单时间,地点都会变.
如果确定了订单表需要拆分, 那么和订单表相关联的订单详情表咋办呢? 没办法跨mysql 服务器进行join的. 那么也要对这种关联紧密的相关表进行拆分. 而且还有诸如, 订单状态表这种, 数据量不大, 但是所有的订单表都需要查询的订单的状态, 这种情况下, 可以做成全局表,所有的节点共存一份.

分表实践

前提条件

你要有两台mysql服务器并且没有主从复制, 我是用docker 启动的两台mysql 服务器.如果你有更多的也行, 最起码要有两台.

数据准备

分别在你的两台mysql 服务器上执行以下的sql 脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
create DATABASE monica CHARACTER set utf8 ;
use monica ;
-- 分表
create table `order` (
`id` int PRIMARY key ,
price varchar (10),
customer_id varchar(10)
);

-- 用于 EP 表
create table `order_view` (
`id` int PRIMARY key ,
price varchar (10),
order_id int,
CONSTRAINT fk_order_orderview FOREIGN key (`order_id`) REFERENCES `order`(`id`)
);

-- 全局表
create table order_status (
`id` int PRIMARY key,
`status` varchar(10)
);

修改配置文件

修改 schema.xml

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
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
<!-- 需要被拆分的表, 分到哪个节点上, 切片的规则是什么, 这里的切片规则是自定义的名字,要记住它, 必须要和rule.xml 里的一致 -->
<table name="order" dataNode="dn1,dn2" rule="mod_rule" >
<!-- childTable 就是和被切分的表关联紧密的表, parentKey是 关联的主表的主键-->
<childTable name="order_view" primaryKey="id" joinKey="order_id" parentKey="id"/>
</table>
<!-- type="global" 是全局表,所有的节点都会保存数据 -->
<table name="order_status" dataNode="dn1,dn2" type="global" ></table>
</schema>
<dataNode name="dn1" dataHost="host1" database="monica" />
<dataNode name="dn2" dataHost="host2" database="monica" />
<dataHost name="host1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="172.17.0.3:3306" user="root"
password="">
</writeHost>
</dataHost>
<dataHost name="host2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM2" url="172.17.0.4:3306" user="root"
password="">
</writeHost>
</dataHost>
</mycat:schema>

修改rule.xml

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
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://io.mycat/">
<!-- 复制一个<tableRule 标签并修改即可 -->
<tableRule name="mod_rule">
<rule>
<!-- 被切分的表的切分字段 -->
<columns>customer_id</columns>
<!-- 切片规则,下面就有现成的,我们拿的是这个取摸的, mod-log 的取模数, 要自己往下面找,并修改即可 -->
<algorithm>mod-long</algorithm>
</rule>
</tableRule>

<tableRule name="rule1">
<rule>
<columns>id</columns>
<algorithm>func1</algorithm>
</rule>
</tableRule>
......................
<function name="rang-long"
class="io.mycat.route.function.AutoPartitionByLong">
<property name="mapFile">autopartition-long.txt</property>
</function>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">2</property>
</function>

重启mycat 容器

docker restart 容器ID 即可

验证

必须通过mycat 来分配数据,不能自己去mysql 服务器上执行插入操作, 是无效的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
--  全局插入
insert into order_status (`id`, `status`) values (0, "finish"), (1, "cancle"),(2, "create");

-- 取模插入
-- 单个节点
insert into `order`(id, price, customer_id) values (0, "102.6", "007" ),(1, "103.6", "005" ), (2, "103.6", "003" );
-- 单个节点
insert into `order`(id, price, customer_id) values (3, "102.6", "002" ),(4, "103.6", "004" ), (5, "103.6", "006" );
-- 多个节点的同时插入,mycat 会把数据拆分插入到不同的节点上
insert into `order`(id, price, customer_id) values (10, "102.6", "101" ),(11, "103.6", "102" ), (12, "103.6", "103" );

-- ChildTable multi insert not provided ChildTable 只能单条插入
insert into `order_view`(id, price, order_id) values (3,"103.6", 3 );

ChildTable 的数据只能单条插入.
还有, 既然有了分表的区分字段, 那么通过mycat 插入数据的时候,一定要写上字段名, 不然mycat无法判断.