相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中 包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就 是将表中的某些行切分 到一个数据库,而另外的某些行又切分到其他的数据库中,如图:
新增:
#配置分表规则方法
④修改rule.xml
1.新增规则类
<tableRule name="mod_rule">
<rule>
<columns>customer_id</columns> #通过顾客id分库
<algorithm>mod-long</algorithm> #策略为取模
</rule>
</tableRule>
2.新增规则方式
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">2</property> #表示取模2
</function>
⑤重启mycat服务,登录mycat用户
⑥mycat插入语句测试
INSERT INTO orders(id,order_type,customer_id,amount) VALUES (1,101,100,100100);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(2,101,100,100300);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(3,101,101,120000);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(4,101,101,103000);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(5,102,101,100400);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(6,102,100,100020);
⑦测试效果
分别查看mysql服务器1和mysql服务器2,orders库中的orders表,确实是通过取模2来的
2.带上join分表实现原理
Orders 订单表已经进行分表操作了,和它关联的 orders_detail 订单详情表如何进行 join 查询。我们要对 orders_detail 也要进行分片操作。Join 的原理如下图:
1、 ER 表
Mycat 借鉴了 NewSQL 领域的新秀 Foundation DB 的设计思路,Foundation DB 创新性的提 出了 Table Group 的概念,其将子表的存储位置依赖于主表,并且物理上紧邻存放,因此彻底解决了 JION 的效率和性能问 题,根据这一思路,提出了基于 E-R 关系的数据分片策略,子表的记录与所 关联的父表记录存放在同一个数据分片上。
①另外一张dn2创建表
CREATE TABLE orders_detail(
id INT AUTO_INCREMENT,
detail VARCHAR(2000),
order_id INT,
PRIMARY KEY(id)
);
②修改配置文件schuma.xml
③访问 Mycat 向 orders_detail 表插入数据
INSERT INTO orders_detail(id,detail,order_id) values(1,'detail1',1);
INSERT INTO orders_detail(id,detail,order_id) VALUES(2,'detail1',2);
INSERT INTO orders_detail(id,detail,order_id) VALUES(3,'detail1',3);
INSERT INTO orders_detail(id,detail,order_id) VALUES(4,'detail1',4);
INSERT INTO orders_detail(id,detail,order_id) VALUES(5,'detail1',5);
INSERT INTO orders_detail(id,detail,order_id) VALUES(6,'detail1',6);
④测试,在mycat、dn1、dn2中运行两个表join语句对比一下
select o.*,od.detail from orders o inner join orders_detail od on o.id=od.order_id;
2.配置全局表
在分片的情况下,当业务表因为规模而进行分片以后,业务表与这些附属的字典表之间的关联, 就成了比较 棘手的问题,考虑到字典表具有以下几个特性:
① 变动不频繁
② 数据量总体变化不大
③ 数据规模不大,很少有超过数十万条记录
鉴于此,Mycat 定义了一种特殊的表,称之为“全局表”,全局表具有以下特性:
① 全局表的插入、更新操作会实时在所有节点上执行,保持各个分片的数据一致性
② 全局表的查询操作,只从一个节点获取
③ 全局表可以跟任何一个表进行 JOIN 操作
将字典表或者符合字典表特性的一些表定义为全局表,则从另外一个方面,很好的解决了数据 JOIN 的难题。通过全局表+基于 E-R 关系的分片策略,Mycat 可以满足 80%以上的企业应用开发
2.1修改schema.xml文件
2.2在dn2中创建字典表
#订单状态字典表 rows:20
CREATE TABLE dict_order_type(
id INT AUTO_INCREMENT,
order_type VARCHAR(200),
PRIMARY KEY(id)
);
2.3启动mysql服务,重启mycat服务
①service mysql restart
②进入mycat/bin目录,./mycat restart
2.4启动mycat插入语句测试
INSERT INTO dict_order_type(id,order_type) VALUES(101,‘type1’);
INSERT INTO dict_order_type(id,order_type) VALUES(102,‘type2’);
全局表配置完成
3.常用的分片规则
1.取模方式(详情在分表处)
2.分片枚举
通过在配置文件中配置可能的枚举 id,自己配置分片,本规则适用于特定的场景,比如有些业务需要按照省份或区县来做保存,而全国省份区县固定的,这类业务使用本条规则。
①修改schema.xml文件
②修改rule.xml
(1)修改对应规则分片字段
<tableRule name="sharding_by_intfile">
<rule>
<columns>areacode</columns> #分片字段
<algorithm>hash-int</algorithm> #分片函数
</rule>
</tableRule>
(2)修改对应分片规则hash-int
<function name="hash-int"
class="io.mycat.route.function.PartitionByFileMap">
<property name="mapFile">partition-hash-int.txt</property>#文件名称
<property name="type">1</property>#0为int 1为String
<property name="defaultNode">0</property>
#defaultNode:默认节点:小于 0 表示不设置默认节点,大于等于 0 表示设置默认节点,
#设置默认节点如果碰到不识别的枚举值,就让它路由到默认节点,如不设置不识别就报错
③修改partition-hash-int.txt文件
110=0
120=1
④重启mycat服务,重新进入mycat
(1)./mycat restart
(2)mysql -umycat -p -h192.168.119.131 -P8066
⑤使用mycat创建表
CREATE TABLE orders_ware_info
(
`id` INT AUTO_INCREMENT comment '编号',
`order_id` INT comment '订单编号',
`address` VARCHAR(200) comment '地址',
`areacode` VARCHAR(20) comment '区域编号',
PRIMARY KEY(id)
);
⑥插入语句
INSERT INTO orders_ware_info(id, order_id,address,areacode) VALUES (1,1,'北京','110');
INSERT INTO orders_ware_info(id, order_id,address,areacode) VALUES (2,2,'天津','120');
⑦测试dn1,dn2测试是否成功通过内容分片了
select * from orders_ware_info
3.范围约定
①修改schema.xml
> <table name="payment_info" dataNode="dn1,dn2" rule="auto_sharding_long" ></table>
②修改rule.xml
<tableRule name="auto_sharding_long">
<rule>
<columns>order_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>
<property name="defaultNode">0</property>
</function>
#mapFilie 标识配置文件名称
#defaultNode 默认节点小于0表示不设置默认节点,大于等于0表示设置默认节点
设置默认节点如果喷到不识别的枚举值,就让它路由到默认节点,如不设置不识别就报错
③修改autopartition-long.xml
0-102=0
103-200=1
④验证数据
1.启动mycat,并执行创建表
支付信息表
CREATE TABLE payment_info
(
id
INT AUTO_INCREMENT comment ‘编号’,
order_id
INT comment ‘订单编号’,
payment_status
INT comment ‘支付状态’,
PRIMARY KEY(id)
);
2.插入数据
INSERT INTO payment_info (id,order_id,payment_status) VALUES (1,101,0);
INSERT INTO payment_info (id,order_id,payment_status) VALUES (2,102,1);
INSERT INTO payment_info (id,order_id ,payment_status) VALUES (3,103,0);
INSERT INTO payment_info (id,order_id,payment_status) VALUES (4,104,1);
3验证结果,查看两张表看看是否根据范围进行分表的
select * from payment_info;
4.时间分片
①修改schema.xml
②修改rule.xml
login_date
shardingByDate
<function name="shardingByDate" class="io.mycat.route.function.PartitionByDate">
<property name="dateFormat">yyyy-MM-dd</property>
<property name="sBeginDate">2019-01-01</property>
<property name="sEndDate">2019-01-04</property>
<property name="sPartionDay">2</property>
</function>
columns:分片字段,algorithm:分片函数
#dateFormat :日期格式
#sBeginDate :开始日期
#sEndDate:结束日期,则代表数据达到了这个日期的分片后循环从开始分片插入
#sPartionDay :分区天数,即默认从开始日期算起,分隔 2 天一个分区
③验证数据
1.#创建用户信息表
CREATE TABLE login_info
(
id
INT AUTO_INCREMENT comment ‘编号’,
user_id
INT comment ‘用户编号’,
login_date
date comment ‘登录日期’,
PRIMARY KEY(id)
);
2.#插入数据
INSERT INTO login_info(id,user_id,login_date) VALUES (1,101,‘2019-01-01’);
INSERT INTO login_info(id,user_id,login_date) VALUES (2,102,‘2019-01-02’);
INSERT INTO login_info(id,user_id,login_date) VALUES (3,103,‘2019-01-03’);
INSERT INTO login_info(id,user_id,login_date) VALUES (4,104,‘2019-01-04’);
INSERT INTO login_info(id,user_id,login_date) VALUES (5,103,‘2019-01-05’);
INSERT INTO login_info(id,user_id,login_date) VALUES (6,104,‘2019-01-06’);
3.#查dn1,dn2数据
select * from login_info;