什麼是水平切分?
當業務數據庫單表記錄在千萬級別以上,我們通過讀寫分離和垂直切分也無法解決數據庫單褲讀寫與存儲的性能瓶頸,這時隨着業務數據不斷快速增長,就必須對數據庫中的表做水平切分,相對於垂直拆分,水平切分不是將表做分類,而是按照某個字段的某種規則來分散到多個庫之中,每個表中包含一部分數據。簡單來說,我們可以將數據的水平切分理解爲是按照數據行的切分,就是將表中的某些行切分到一個數據庫,而另外的某些行又切分到其他的數據庫中,如圖:
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分庫分表功能,對數據庫表實現水平切分,解決大數據量下表的讀寫性能問題,碼字不易,如果覺得有所幫助請點贊支持,歡迎溝通交流!!!