MyCAT实现分库分表,表水平切分【实例精华】

什么是水平切分?

当业务数据库单表记录在千万级别以上,我们通过读写分离和垂直切分也无法解决数据库单裤读写与存储的性能瓶颈,这时随着业务数据不断快速增长,就必须对数据库中的表做水平切分,相对于垂直拆分,水平切分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中,如图:
在这里插入图片描述

MyCAT分片规则

在数据切分处理中,特别是水平切分中,中间件最终要的两个处理过程就是数据的切分、数据的聚合。选择合适的切分规则,至关重要,因为它决定了后续数据聚合的难易程度,甚至可以避免跨库的数据聚合处理。要在最大的程度上的合理切分表,尽可能避免跨数据库join的情况,这里就讲讲MyCAT的切分规则。

Mycat全局表

如果你的业务中有些数据类似于数据字典,比如配置文件的配置,常用业务的配置或者数据量不大很少变动的表,这些表往往不是特别大,而且大部分的业务场景都会用到,那么这种表适合于Mycat全局表,无须对数据进行切分,只要在所有的分片上保存一份数据即可,Mycat 在Join操作中,业务表与全局表进行Join聚合会优先选择相同分片内的全局表join,避免跨库Join,在进行数据插入操作时,mycat将把数据分发到全局表对应的所有分片执行,在进行数据读取时候将会随机获取一个节点读取数据。目前Mycat没有做全局表的数据一致性检查,后续版本1.4之后可能会提供全局表一致性检查,检查每个分片的数据一致性。全局表的配置如下

<!--定义全局表 每个数据库都会保存一份,type="global-->
<table name="item" dataNode="dn1,dn2,dn3" type="global"/>

注:type="global"声明这是一张全局表

ER分片表

ER表又称为父子表,代表了关系数据库中一对多的数据逻辑,也就是说会存在表的主从关系,这类业务的切分可以抽象出合适的切分规则,总之部分业务总会可以抽象出父子关系的表。这类表适用于ER分片表,子表的记录与所关联的父表记录存放在同一个数据分片上,避免数据Join跨库操作。以order与order_detail例子为例,schema.xml中定义如下的分片配置,order,order_detail 根据order_id进行数据切分,保证相同order_id的数据分到同一个分片上,在进行数据插入操作时,Mycat会获取order所在的分片,然后将order_detail也插入到order所在的分片。

<!--定义ER父子表关系,父子表根据逻辑rule配置切分在一个数据库 -->
		<table name="cat_parent" dataNode="dn1,dn2,dn3" rule="auto-sharding-long">
			<!--定义子表 name=子表名称 joinkey=子表关联父表外键 parentKey=父表主键字段-->
			<childTable name="cat_son" primaryKey="ID" joinKey="parentID" parentKey="ID"/>
		</table>

注:MyCAT目前无法解决多对多表关系,即表A和表B通过中间表关联,只能根据整体业务逻辑倾向的表来创建

全局序列号

当数据切分后,原有的关系数据库中的主键约束在分布式条件下将无法使用(表切分在不同的数据库库,主键ID会重复),因此需要引入外部机制保证数据唯一性标识,这种保证全局性的数据唯一标识的机制就是全局序列号(sequence)。
MyCAT保证全局系列号一致性主要有以下几种方式:

  • 本地文件方式(文件配置,自定义序列号,定义最大值,最小值等参数)
  • 数据库配置(基于数据库CAS锁机制,原子性获取新增序列号)
  • 使用catelet注解方式(MyCAT注解方式)
  • 自增长主键设置(通过table定义主键自增长)

这里重点说明自增长主键设置,在table配置中定义primaryKey主键名,autolncrement开启主键自增长

<!-- table 逻辑表  name=逻辑表名(mycat表名) dataNode=逻辑节点(物理分片) rule=分片规则 auto-sharding-long(按照id值划分) 默认每500万为一个分片-->
		<table name="cat_test" dataNode="dn1,dn2,dn3" autolncrement="true" primaryKey="ID" rule="auto-sharding-long" />

MyCAT分片规则

这里不做过多的解释,所谓分片规则就是针对表进行分片时采用哪种计算方法,不同的表可以根据业务逻辑的特殊性,采用适合的分片规则,同时分片规则也可以自定义进行修改,MyCAT提供以下几种默认的分片算法:

  • 分片枚举
  • 固定分片hash算法
  • 求模算法
  • 按日期分片
  • 取模范围约束
  • ASCII码求模范围约束
  • 字符串子串截取分片
  • 字符串hash解析
  • 一致hash
  • 按单月小时拆分
  • 自然月分片

这里列举了MyCAT分片规则,因为篇幅的关系并不细说每种规则适合的场景,后续有机会在详细举例。

实例搭建,schema.xml配置水平切分

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://org.opencloudb/">

<!-- schema 逻辑库(可以定义水平拆分表,也可以定义垂直拆分表) name="mycat逻辑库名" -->
	<schema name="MYCATDB" checkSQLschema="false" sqlMaxLimit="100">
		<!-- 水平切分,定义切片规则rule -->
		<!-- table 逻辑表  name=逻辑表名(mycat表名) dataNode=逻辑节点(物理分片) rule=分片规则 auto-sharding-long(按照id值划分) 默认每500万为一个分片-->
		<table name="cat_test" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
		<!-- table 逻辑表 name=逻辑表名 dataNode=逻辑节点(物理分片) rule=分片规则 sharding-by-murmur-order_id 自定义一致hash算法 针对表主键不是id -->
		<table name="cat_order" dataNode="dn1,dn2,dn3" rule="sharding-by-murmur-order_id" />
		
		<!--定义ER父子表关系,父子表根据逻辑rule配置切分在一个数据库 -->
		<table name="cat_parent" dataNode="dn1,dn2,dn3" rule="auto-sharding-long">
			<!--定义子表 name=子表名称 joinkey=子表关联父表外键 parentKey=父表主键字段-->
			<childTable name="cat_son" joinKey="parentID" parentKey="id"/>
		</table>
		
		<!--垂直切分 没有分片规则rule-->
		<table name="ver_01" dataNode="dn1" />
		<table name="ver_02" dataNode="dn2"/>
		<table name="ver_03" dataNode="dn3"/>
		<table name="ver_04" dataNode="dn3"/>
		<!--定义全局表 每个数据库都会保存一份,type="global-->
		<table name="item" dataNode="dn1,dn2,dn3" type="global"/>
	</schema>
	
	<!--定义主从同步数据库,mycat做读写分离,只能绑定一个dataNota-->
	<schema name="KEVINDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="sync1">
	</schema>
	
	<!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743" 
		/> -->
		<!--逻辑节点配置 name节点名称  dataHost节点主机(物理节点) database 节点数据库(物理数据库名) -->
	<dataNode name="dn1" dataHost="itcast01" database="db1" />
	<dataNode name="dn2" dataHost="itcast02" database="db2" />
	<dataNode name="dn3" dataHost="itcast02" database="db3" />
	
	<!--独写分离的dataNode database为mysql配置主从同步指定的数据库 kevin-->
	<dataNode name="sync1" dataHost="sync01" database="kevin" />

	<!--
	Balance参数设置:
		1. balance=0, 所有读操作都发送到当前可用的writeHost上。
		2. balance=1”,所有读操作都随机的发送到readHost。
		3. balance=2”,所有读操作都随机的在writeHost、readhost上分发
	WriteType参数设置:
		1. writeType=0, 所有写操作都发送到可用的writeHost上。
		2. writeType=1”,所有写操作都随机的发送到readHost。
		3. writeType=2”,所有写操作都随机的在writeHost、readhost分上发。
	switchType 目前有三种选择:
		-1:表示不自动切换
		1 :默认值,自动切换
		2 :基于MySQL主从同步的状态决定是否切换
		“Mycat心跳检查语句配置为 show slave status ,dataHost 上定义两个新属性: switchType="2" 与slaveThreshold="100",此时意味着开启MySQL主从复制状态绑定的读写分离与切换机制。Mycat心跳机制通过检测 show slave status 中的 "Seconds_Behind_Master", "Slave_IO_Running", "Slave_SQL_Running" 三个字段来确定当前主从同步的状态以及Seconds_Behind_Master主从复制时延。“
-->
<!--配置独写分离的 数据库配置 -->
	<dataHost name="sync01" maxCon="1000" minCon="10" balance="1"
		writeType="0" dbType="mysql" dbDriver="native" switchType="2"  slaveThreshold="100">
		<heartbeat>show slave status</heartbeat>
		<!-- can have multi write hosts -->
		<!-- itcast-01 -->
		<writeHost host="hostM1" url="192.168.79.130:3306" user="root"
			password="root123">
			<!-- can have multi read hosts -->
			<readHost  host="hostS1" url="192.168.79.131:3306" user="root" password="root123"/>
		</writeHost>
	</dataHost> 
	
	<dataHost name="itcast01" 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 -->
		<!-- itcast-01 -->
		<writeHost host="hostM1" url="localhost:3306" user="root"
			password="root123">
			<!-- can have multi read hosts -->
		</writeHost>
	</dataHost> 
	<dataHost name="itcast02" 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 -->
		<!-- itcast-01 -->
		<writeHost host="hostM1" url="192.168.79.131:3306" user="root"
			password="root123">
			<!-- can have multi read hosts -->
		</writeHost>
	</dataHost>
</mycat:schema>

重点关注:“cat_test”;“cat_order”;“cat_parent”三张分片表,其中“cat_test”采用按照表id分片规则,500万为一个分片,“cat_order”采用一致hash算法分片规则,针对主键不是id采用计算hash值分配分片,“cat_parent”采用父子表分片规则,父表根据id分片,子表跟随父表

数据库验证:

验证cat_test表根据id实现分片规则
在itcast-01中查看cat_test分片规则,1-500万分片到db1数据库中
查看db1数据库

mysql> select * from CAT_TEST;
+-------+--------+
| ID    | TITLE  |
+-------+--------+
|     1 | t1     |
|     2 | t2     |
|     3 | t3     |
|     4 | t4     |
|     5 | t5     |
|     6 | t6     |
|     7 | t7     |
| 50001 | t50001 |
+-------+--------+
8 rows in set (0.00 sec)

在itcast-02中查看cat_test分片规则,500-1000万分片到db2数据库中
查看db2数据库

mysql> select * from CAT_TEST;
+---------+----------+
| ID      | TITLE    |
+---------+----------+
| 5000001 | t5000001 |
+---------+----------+
1 row in set (0.00 sec)

由此可见,以每个分片5000000条数据为界限,当ID范围(1-5000000)分配到db1数据库中,(5000001-10000000)分配到db2数据库中,如果数据量超过了可分配的数据库会报错。

验证cat_order表根据一致hash算法划分分片规则
在itcast-01中查看cast_order分片规则,根据一致hash算法
查看db1数据库

mysql> select * from CAT_ORDER LIMIT 5;
+----------+---------+--------------+----------+--------+---------------------+---------------------+--------------+--------------+----------+------------+---------------+---------------+-----------+---------------+------------+------------+--------------------+-----------------+-------------------+-----------+--------+--------------+-------------+-----------+
| ORDER_ID | PAYMENT | PAYMENT_TYPE | POST_FEE | STATUS | CREATE_TIME         | UPDATE_TIME         | PAYMENT_TIME | CONSIGN_TIME | END_TIME | CLOSE_TIME | SHIPPING_NAME | SHIPPING_CODE | USER_ID   | BUYER_MESSAGE | BUYER_NICK | BUYER_RATE | RECEIVER_AREA_NAME | RECEIVER_MOBILE | RECEIVER_ZIP_CODE | RECEIVER  | EXPIRE | INVOICE_TYPE | SOURCE_TYPE | SELLER_ID |
+----------+---------+--------------+----------+--------+---------------------+---------------------+--------------+--------------+----------+------------+---------------+---------------+-----------+---------------+------------+------------+--------------------+-----------------+-------------------+-----------+--------+--------------+-------------+-----------+
|        5 |  299.97 | 1            | NULL     | 0      | 2017-08-24 20:46:11 | 2017-08-24 20:46:11 | NULL         | NULL         | NULL     | NULL       | NULL          | NULL          | lijialong | NULL          | NULL       | NULL       | 金燕龙办公楼       | 13900112222     | NULL              | 李嘉诚    | NULL   | NULL         | NULL        | NULL      |
|        6 |    3.00 | 1            | NULL     | 0      | 2017-08-24 20:46:40 | 2017-08-24 20:46:40 | NULL         | NULL         | NULL     | NULL       | NULL          | NULL          | lijialong | NULL          | NULL       | NULL       | 金燕龙办公楼       | 13900112222     | NULL              | 李嘉诚    | NULL   | NULL         | NULL        | NULL      |
|        8 |  299.97 | 1            | NULL     | 0      | 2017-08-24 20:46:40 | 2017-08-24 20:46:40 | NULL         | NULL         | NULL     | NULL       | NULL          | NULL          | lijialong | NULL          | NULL       | NULL       | 金燕龙办公楼       | 13900112222     | NULL              | 李嘉诚    | NULL   | NULL         | NULL        | NULL      |
|       14 |  299.97 | 1            | NULL     | 0      | 2017-08-24 21:05:56 | 2017-08-24 21:05:56 | NULL         | NULL         | NULL     | NULL       | NULL          | NULL          | lijialong | NULL          | NULL       | NULL       | 金燕龙办公楼       | 13900112222     | NULL              | 李嘉诚    | NULL   | NULL         | NULL        | NULL      |
|       16 |  266.64 | 1            | NULL     | 0      | 2017-08-24 23:07:38 | 2017-08-24 23:07:38 | NULL         | NULL         | NULL     | NULL       | NULL          | NULL          | lijialong | NULL          | NULL       | NULL       | 金燕龙办公楼       | 13900112222     | NULL              | 李嘉诚    | NULL   | NULL         | NULL        | NULL      |
+----------+---------+--------------+----------+--------+---------------------+---------------------+--------------+--------------+----------+------------+---------------+---------------+-----------+---------------+------------+------------+--------------------+-----------------+-------------------+-----------+--------+--------------+-------------+-----------+
5 rows in set (0.00 sec)

在itcast-02中查看cast_order分片规则,根据一致hash算法
查看db2数据库

+----------+---------+--------------+----------+--------+---------------------+---------------------+--------------+--------------+----------+------------+---------------+---------------+-----------+---------------+------------+------------+--------------------+-----------------+-------------------+-----------+--------+--------------+-------------+-----------+
| ORDER_ID | PAYMENT | PAYMENT_TYPE | POST_FEE | STATUS | CREATE_TIME         | UPDATE_TIME         | PAYMENT_TIME | CONSIGN_TIME | END_TIME | CLOSE_TIME | SHIPPING_NAME | SHIPPING_CODE | USER_ID   | BUYER_MESSAGE | BUYER_NICK | BUYER_RATE | RECEIVER_AREA_NAME | RECEIVER_MOBILE | RECEIVER_ZIP_CODE | RECEIVER  | EXPIRE | INVOICE_TYPE | SOURCE_TYPE | SELLER_ID |
+----------+---------+--------------+----------+--------+---------------------+---------------------+--------------+--------------+----------+------------+---------------+---------------+-----------+---------------+------------+------------+--------------------+-----------------+-------------------+-----------+--------+--------------+-------------+-----------+
|        1 |    NULL | 1            | NULL     | 0      | 2017-08-24 20:42:25 | 2017-08-24 20:42:25 | NULL         | NULL         | NULL     | NULL       | NULL          | NULL          | lijialong | NULL          | NULL       | NULL       | ??????             | 13900112222     | NULL              | ???       | NULL   | NULL         | NULL        | NULL      |
|        2 |    NULL | 1            | NULL     | 0      | 2017-08-24 20:44:03 | 2017-08-24 20:44:03 | NULL         | NULL         | NULL     | NULL       | NULL          | NULL          | lijialong | NULL          | NULL       | NULL       | ??????             | 13900112222     | NULL              | ???       | NULL   | NULL         | NULL        | NULL      |
|        3 |    3.00 | 1            | NULL     | 0      | 2017-08-24 20:46:10 | 2017-08-24 20:46:10 | NULL         | NULL         | NULL     | NULL       | NULL          | NULL          | lijialong | NULL          | NULL       | NULL       | ??????             | 13900112222     | NULL              | ???       | NULL   | NULL         | NULL        | NULL      |
|        9 |    3.00 | 1            | NULL     | 0      | 2017-08-24 21:01:10 | 2017-08-24 21:01:10 | NULL         | NULL         | NULL     | NULL       | NULL          | NULL          | lijialong | NULL          | NULL       | NULL       | 金燕龙办公楼       | 13900112222     | NULL              | 李嘉诚    | NULL   | NULL         | NULL        | NULL      |
|       10 |  266.64 | 1            | NULL     | 0      | 2017-08-24 21:01:11 | 2017-08-24 21:01:11 | NULL         | NULL         | NULL     | NULL       | NULL          | NULL          | lijialong | NULL          | NULL       | NULL       | 金燕龙办公楼       | 13900112222     | NULL              | 李嘉诚    | NULL   | NULL         | NULL        | NULL      |
+----------+---------+--------------+----------+--------+---------------------+---------------------+--------------+--------------+----------+------------+---------------+---------------+-----------+---------------+------------+------------+--------------------+-----------------+-------------------+-----------+--------+--------------+-------------+-----------+
5 rows in set (0.00 sec)

由此可见一致hash算法,是通过计算指定字段的hash值来决定分片,指定hash字段配置在rule.xml中,这里指定了order_id为hash计算字段

<!--自定义一致哈希算法分片 -->
	<tableRule name="sharding-by-murmur-order_id">
		<rule>
			<columns>order_id</columns>
			<algorithm>murmur</algorithm>
		</rule>
	</tableRule>

这里基本完成了MyCAT分库分表功能,对数据库表实现水平切分,解决大数据量下表的读写性能问题,码字不易,如果觉得有所帮助请点赞支持,欢迎沟通交流!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章