四、Mycat分片技术
4.1 垂直拆分-分库
前沿
目前很多互联网系统都存在单表数据量过大的问题,这就降低了查询速度,影响了客户体验。为了提高查询速度,我们可以优化sql语句,优化表结构和索引,不过对那些百万级千万级的数据库表,即便是优化过后,查询速度还是满足不了要求。
垂直分库
就是将不同业务的数据表存放到不同的数据库服务器中。
实现分库
我们选择master1和slave1作为存放不同表的服务器。但是需要注意的是这两个服务器是存在主从复制的,我们需要将这两个服务器的主从复制关掉。
mysql> stop slave; #查看主从复制是否关闭 show slave status \G;
修改mycat配置文件schema.xml
<?xml version="1.0"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <mycat:schema xmlns:mycat="http://io.mycat/"> <!-- 数据库配置 与server.xml 中的数据库对应 --> <schema name="db_test" checkSQLschema="false" dataNode="dn1" sqlMaxLimit="100"> <!--如果是customer表就存放在dn2节点上,其他的表存放在dn1数据节点上--> <table name="customer" dataNode="dn2"/> </schema> <!-- 两个数据节点 dn1,2 上都要创建 orders 数据库 --> <dataNode name="dn1" dataHost="host1" database="orders" /> <dataNode name="dn2" dataHost="host2" database="orders" /> <!-- 两个数据库 肯定两个 dataHost --> <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="192.168.66.100:3350" user="root" password="123456"> </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="192.168.66.100:3360" user="root" password="123456"> </writeHost> </dataHost> </mycat:schema>
启动/重新启动mycat
mycat start/restart
开启3350和3360两个docker容器master1和slave1(此时两者不具备主从复制关系).
docker start master1 docker start slave1 #分别进入到容器 docker exec -it master1 /bin/bash docker exec -it slave1/bin/bash #分别进入两个数据库 mysql -uroot -p123456 #分别创建orders表 mysql> create database orders;
进入mycat,并创建表验证垂直分库
mysql -uroot -p123456 -h192.168.66.100 -P8066
create table customer( id int auto_increment, name varchar(200), primary key(id) ); create table orders( id int auto_increment, order_type int, customer_id int, amount decimal(10,2), primary key(id) ); create table orders_detail( id int auto_increment, order_id int, detail varchar(200), primary key(id) ); create table dict_order_type( id int auto_increment, order_type varchar(200), primary key(id) );
master1节点中的数据表
mysql> show tables; +------------------+ | Tables_in_orders | +------------------+ | dict_order_type | | orders | | orders_detail | +------------------+ 3 rows in set (0.00 sec)
slave1节点中的数据表
mysql> show tables; +------------------+ | Tables_in_orders | +------------------+ | customer | +------------------+ 1 row in set (0.00 sec)
综上,垂直分库成功!
4.2 水平拆分-分表
前言
上面谈到垂直切分只是把表按模块划分到不同数据库,但没有解决单表大数据量的问题。
水平拆分
相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分数据。
理解:
我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中。
实现分表
1、 选择要拆分的表
MySQL 单表存储数据条数是有瓶颈的,单表达到 1000 万条数据就达到了瓶颈,会影响查询效率,需要进行水平拆分(分表)进行优化。
2、分表字段
以 orders 表为例,可以根据不同字段进行分表
编号 | 分表字段 | 效果 |
1 | id | 查询订单注重时效,历史订单被查询的次数少,如此分片会造成一个节点访问多,一个访问少,不平均。 |
2 | customer_id | 根据客户 id 去分,两个节点访问平均。 |
3、 修改配置文件 schema.xml
为 orders 表设置数据节点为 dn1、dn2,并指定分片规则为 mod_rule(自定义的名字)
<?xml version="1.0"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <mycat:schema xmlns:mycat="http://io.mycat/"> <schema name="TESTDB" checkSQLschema="false" dataNode="db_node1" sqlMaxLimit="100"> <table name="orders" dataNode="db_node1,db_node2" rule="mod_rule"/> </schema> <dataNode name="db_node1" dataHost="db_host1" database="orders" /> <dataNode name="db_node2" dataHost="db_host2" database="orders" /> <dataHost name="db_host1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <writeHost host="hostM1" url="192.168.66.100:3350" user="root" password="123456"> </writeHost> </dataHost> <dataHost name="db_host2" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <writeHost host="hostM1" url="192.168.66.100:3360" user="root" password="123456"> </writeHost> </dataHost> </mycat:schema>
4、修改配置文件rule.xml
分片规则:
在 rule 配置文件里新增分片规则 mod_rule,并指定规则适用字段为 customer_id, 还有选择分片算法 mod-long(对字段求模运算),customer_id 对两个节点求模,根据结果分片.配置算法 mod-long 参数 count 为 2,两个节点。
<tableRule name="mod_rule"> <rule> <columns>customer_id</columns> <algorithm>mod-long</algorithm> </rule> </tableRule> <function name="mod-long" class="io.mycat.route.function.PartitionByMod"> <!-- 物理主机的节点数 --> <property name="count">2</property> </function>
5、 重启Mycat,让配置生效
6、 在数据节点dn1,dn2上建orders表
create table orders( id int auto_increment, order_type int, customer_id int, amount decimal(10,2), primary key(id) );
7、 连接mycat并添加数据
mysql -uroot -p123456 -h192.168.66.100 -P8066
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);
在mycat中可以看到全部的6条数据,但是在两个节点中只能看到3条数据。
4.3 ER表
分片 join
在上一小结主要是实现了数据表的水平拆分,但是在做表和表之间的连接查询的时候会出问题,假设我们用orders表和orders_details表做关联查询,sql发送到mycat的时候发现orders表做水平拆分后出现在好几个数据库服务器中,于是就将sql发送给这些服务器,但是有些服务器不存在orders_detilas表,执行sql会返回错误。这就是分片join错误。
ER 表
将子表的存储位置依赖于主表,并且物理上紧邻存放因此彻底解决了JION的效率和性能问题,根据这一思路,提出了基于E-R关系的数据分片策略,子表的记录与所关联的父表记录存放在同一个数据分片上。说人话就是我们将表和表之间基于某种关系或指定分为子表和父表,mycat会将子表的数据存放在含有父表的数据库服务器上。
修改配置文件schema.xml配置父子关系
<schema name="TESTDB" checkSQLschema="false" dataNode="db_node1" sqlMaxLimit="100"> <table name="orders" dataNode="db_node1,db_node2" rule="mod_rule"> #childTable:子表 <childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" /> </table> </schema>
重启Mycat服务
mycat restart