關系型數據的分布式處理系統MyCAT(2)
——高級功能和管理監控
日期:2014/12/24
文:阿蜜果
1、 高級功能
1.1 分片策略
MyCAT通過定義表的分片規則來實現分片,每個表格可以捆綁一個分片規則,每個分片規則指定一個分片字段并綁定一個函數,來實現動態分片算法。
1.1.1 分片規則
以常用的基于整數映射的分片函數org.MyCAT.route.function.PartitionByFileMap為例,此函數通過一個配置文件來確定映射關系,以下面的sharding-by-intfile這個分片規則為例:
<tableRule name="sharding-by-intfile">
<rule>
<columns>sharding_id</columns>
<algorithm>hash-int</algorithm>
</rule>
</tableRule>

<function name="hash-int" class="org.opencloudb.route.function.PartitionByFileMap">
<property name="mapFile">partition-hash-int.txt</property>
</function>
conf目錄下的partition-hash-int.txt文件的默認內容如下所示:
10000=0
10010=1
表明當字段user_id取值為10000的時候,返回分片節點ID為0,以此類推。
Schema.xml中定義customer表的分片規則為此規則:
<table name="t_user" dataNode=" user0,user1,user2,user3" rule="sharding-by-intfile" />
于是t_user按照字段user_id進行水平分片,分片存儲在四個數據節點user1、user2、user3和user4上。
使用MySQL客戶端連接mycat的test_mycat數據庫,分別運行如下插入語句:
INSERT INTO t_user (user_id, receive_address, create_time, province_code) VALUES ('10000', '廣州市越秀區廣州大道中599號', '2014-12-25 16:00:00', 'GD');
INSERT INTO t_user (user_id, receive_address, create_time, province_code) VALUES ('10010', '廣州市越秀區廣州大道中599號', '2014-12-25 16:00:00', 'GD');
INSERT INTO t_user (user_id, receive_address, create_time, province_code) VALUES ('10011', '廣州市越秀區廣州大道中599號', '2014-12-25 16:00:00', 'GD');
連接localhost的user0 ~user3四個數據庫,查看t_user表的數據后可得知user_id為10000和10010的記錄分別進入user0和user1數據庫,最后一條運行記錄因為根據partition-hash-int.txt的配置無法確定寫入那個數據庫節點,因為報錯:
[Err] 1003 - can't find datanode for sharding column:USER_ID val:10011
如何知道某個SQL在哪個分片上執行? 用explain sql語句:
explain select * from t_user where user_id=10000 or user_id=10010;
在MySQL客戶端運行,結果如下圖所示:
運行如下explain sql語句:
explain select * from t_user;
在MySQL客戶端運行,結果如下圖所示:
根據業務場景和數據特點,可以選用以下的分片規則:
l auto-sharding-long 主鍵自動增長的數字,按照范圍進行自動分片,比如0-200萬的數據在分片節點0,200-400萬的數據在分片節點2,依次類推,根據數據庫服務器的性能,可以每個分片存儲100-500條記錄之間,此種方式,每個分片表一個獨立的自增長ID機制,確保記錄的連續性。conf/ autopartition-long.txt中定義了分段范圍與分片ID的關系。
rule.xml的參考配置為:
<tableRule name="auto-sharding-long">
<rule>
<columns>user_id</columns>
<algorithm>rang-long</algorithm>
</rule>
</tableRule>

<function name="rang-long" class="org.opencloudb.route.function.AutoPartitionByLong">
<property name="mapFile">autopartition-long.txt</property>
</function>
對應的范圍設置在conf/autopartition-long.txt文件,參考內容如下:
# range start-end ,data node index
# K=1000,M=10000.
0-10000=0
10001-20000=1
20001-30000=2
30000-40000=3

在schema.xml更改t_user表的rule到auto-sharding-long,重啟MyCAT后,使用MySQL連接后運行如下SQL語句:
INSERT INTO t_user(user_id, receive_address, create_time, province_code) VALUES ('9999', '廣州市越秀區廣州大道中599號', '2014-12-25 17:00:00', 'GD');
INSERT INTO t_user(user_id, receive_address, create_time, province_code) VALUES ('10001', '廣州市越秀區廣州大道中599號', '2014-12-25 17:00:00', 'GD');
INSERT INTO t_user(user_id, receive_address, create_time, province_code) VALUES ('20001', '廣州市越秀區廣州大道中599號', '2014-12-25 17:00:00', 'GD');
INSERT INTO t_user(user_id, receive_address, create_time, province_code) VALUES ('30001', '廣州市越秀區廣州大道中599號', '2014-12-25 17:00:00', 'GD');
分別查詢user0~user3數據庫,可看到user_id分別為9999、10001、20001和30001的記錄分別被插入到user0、user1、user2和user3數據庫。
l mod-long,對某些表,我們基本上很少會涉及到范圍查詢的,只根據某個字段(最常見是主鍵)進行查找定位,則可以用求余的方式,隨機分配到其中一個節點上。
rule.xml的參考配置為:
<tableRule name="mod-long">
<rule>
<columns>user_id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>

<function name="rang-long" class="org.opencloudb.route.function.AutoPartitionByLong">
<property name="mapFile">autopartition-long.txt</property>
</function>
在schema.xml更改t_user表的rule到mod-long,重啟MyCAT后,使用MySQL連接后運行如下SQL語句:
INSERT INTO t_user (user_id, receive_address, create_time, province_code) VALUES (10004, '廣州市越秀區廣州大道中599號', '2014-12-25 17:30:00', 'GD');
INSERT INTO t_user (user_id, receive_address, create_time, province_code) VALUES (10005, '廣州市越秀區廣州大道中599號', '2014-12-25 17:30:00', 'GD');
INSERT INTO t_user (user_id, receive_address, create_time, province_code) VALUES (10006, '廣州市越秀區廣州大道中599號', '2014-12-25 17:30:00', 'GD');
INSERT INTO t_user (user_id, receive_address, create_time, province_code) VALUES (10007, '廣州市越秀區廣州大道中599號', '2014-12-25 17:30:00', 'GD');
分別查詢user0~user3數據庫,可看到user_id分別為10004~10007的記錄分別被插入到user0、user1、user2和user3數據庫。
所有的分片規則都在rule.xml中定義,不同的表根據需求,定義不同的分片規則。
對于某些不分片的表格,或者分片規則一樣的表格,table的定義可以用簡化的方式來寫,如:<table name="t_user,t_user_class_rel" rule="auto-sharding-long" /> 。對于此種方式,name中定義的這些表格具有相同的屬性,并且都不能有childTable 元素。
1.1.2 基于E-R關系分片策略
傳統的數據庫分片方式都是基于單個表格,對于表關聯這種操作,則很難處理。為了能夠執行t_user與t_user_class_rel的聯合查詢, MyCAT借鑒了NewSQL領域的新秀Foundation DB的設計思路,Foundation DB創新性的提出了Table Group的概念,其將子表的存儲位置依賴于主表,并且物理上緊鄰存放,因此徹底解決了JOIN的效率和性能問題,根據這一思路,提出了基于E-R關系的數據分片策略,子表的記錄與所關聯的父表記錄存放在同一個數據分片上。
以t_user與t_user_class_rel例子為例,schema.xml中定義如下的分片配置:
<table name="t_user" dataNode="user0,user1,user2,user3" rule="mod-long">
<childTable name="t_user_class_rel" primaryKey="id" joinKey="user_id" parentKey="user_id" />
</table>
t_user采用mod-long這個分片策略,分片在user0~user3上,t_user_class_rel依賴父表進行分片,兩個表的關聯關系為t_user_class_rel.user_id=t_user.id。于是數據分片和存儲的示意圖如下:
這樣一來,分片user0上的t_user與user0上的t_user_class_rel就可以進行局部的JOIN聯合,user1~user3上也如此,再合并兩個節點的數據即可完成整體的JOIN,試想一下,每個分片上t_user_class_rel表有1000萬條,則10個分片就有1個億,基于E-R映射的數據分片模式,基本上解決了80%以上的企業應用所面臨的問題。
多對多的表格如何處理?多對多的表格通常情況下,有以下幾種:
l 主表+關系表+字典表
l 主表A+關系表+主表B
對于第一種,字典表可以被定義為“全局表”,字典表的記錄規模可以在幾千到幾十萬之間,基本是變動比較少的表,由MyCAT自動實時同步到所有分片,這樣就可以三個表都做JOIN操作了。
對于第二種,需要從業務角度來看,關系表更偏向哪個表,即“A的關系”還是“B的關系”,來決定關系表跟從那個方向存儲。目前還暫時無法很好支持這種模式下的3個表之間的關聯。未來版本中將考慮將中間表進行雙向復制,以實現從A-關系表 以及B-關系表的雙向關聯查詢。
關于全局表的實現方式,全局表在數據插入或更新的時候,會自動在全局表定義的所有數據節點上執行相同的操作,以保證所有數據節點都一致,由于這個特性,全局表可以跟任何分片或不分片的表格進行JOIN操作。對數據更新不頻繁的,規模不是很大的(100萬之內)的表都可以定義為MyCAT的全局表,以實現用存儲換性能的目標。
1.1.3 主鍵分片 VS 非主鍵分片
主鍵分片還是非主鍵分片,這個問題并不是很難,當你沒人任何字段可以作為分片字段的時候,主鍵分片就是唯一選擇,其優點是按照主鍵的查詢最快,當采用自動增長的序列號作為主鍵時,還能比較均勻的將數據分片在不同的節點上。
若有某個合適的業務字段比較合適作為分片字段,則建議采用此業務字段分片,選擇分片字段的條件如下:
- 盡可能的比較均勻分布數據到各個節點上;
- 該業務字段是最頻繁的或者最重要的查詢條件。
常見的除了主鍵之外的其他可能分片字段有“訂單創建時間”、“店鋪類別”或“所在省”等。當你找到某個合適的業務字段作為分片字段以后,不必糾結于“犧牲了按主鍵查詢記錄的性能”,因為在這種情況下,MyCAT提供了“主鍵到分片”的內存緩存機制,熱點數據按照主鍵查詢,絲毫不損失性能。做法如下:
<table name="t_user" primaryKey="user_id" dataNode="user0,user1,user2,user3" rule="mod-long">
<childTable name="t_user_class_rel" primaryKey="id" joinKey="user_id" parentKey="user_id" />
</table>
對于非主鍵分片的table,填寫屬性primaryKey,此時MyCAT會將你根據主鍵查詢的SQL語句的第一次執行結果進行分析,確定該Table 的某個主鍵在什么分片上,并進行主鍵到分片ID的緩存,以下面SQL為例,由于id 不是orders的分片字段,因此這個SQL第一次會發送給所有分片去執行:
select * from orders where id=1;
對于多個主鍵的查詢,一樣可以自動優化,如:
select * from orders where id in (1,2,3)
則會分別存儲1、2、3這三個主鍵到分片的緩存關系。
設想下,每個表有5000萬數據,10%的熱點數據經常按照主鍵查詢,5000萬*10%=500萬,緩存上述信息大概需要1.5G內存,通過分析緩存使用信息,就可以最精確的調優這筆緩存的內存。通過連接MyCAT的9066管理端口,執行show @@cache,可以顯示當前緩存的使用情況。
1.2 高可用性配置
MyCAT支持高可用性的企業級特性,根據應用特性,可以配置如下幾種策略:
l 后端數據庫配置為一主多從,并開啟讀寫分離機制。
l 后端數據庫配置為雙主雙從(多從),并開啟讀寫分離機制。
l 后端數據庫配置為多主多從,并開啟讀寫分離機制
后面兩種配置,具有更高的系統可用性,當其中一個寫節點(主節點)失敗后,Mycat會偵測出來(心跳機制)并自動切換到下一個寫節點,MyCAT在任何時候,只會往一個寫節點寫數據。
下面是典型的雙主雙從的MySQL集群配置:
MyCAT的schema.xml配置文件中的雙主雙從參考配置:
<dataHost name="testhost" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="hostM1" url="localhost:3306" user="root" password="">
<readHost host="hostM2" url="10.18.96.144:3306" user="axx" password="axx" />
</writeHost>
<writeHost host="hostM3" url="……:3306" user="root" password="">
<readHost host="hostM4" url="……:3306" user="axx" password="axx" />
</writeHost>
</dataHost>


2、 管理監控命令
MyCAT自身有類似其他數據庫的管理監控方式,通過Mysql命令行,登錄管理端口(9066)執行相應的SQL,進行管理
mysql -utest -ptest -P9066
show @@help;
此命令會顯示所有的管理監控命令,另外請參照《Mycat命令行監控指南.docx》這個文檔來深入了解。
2.1 監控命令匯總
“show @@help;”命令的運行結果如下所示:
mysql> show @@help;
+--------------------------------------+-----------------------------------+
| STATEMENT | DESCRIPTION |
+--------------------------------------+-----------------------------------+
| clear @@slow where datanode = ? | Clear slow sql by datanode |
| clear @@slow where schema = ? | Clear slow sql by schema |
| kill @@connection id1,id2,
| Kill the specified connections |
| offline | Change MyCat status to OFF |
| online | Change MyCat status to ON |
| reload @@config | Reload all config from file |
| reload @@route | Reload route config from file |
| reload @@user | Reload user config from file |
| rollback @@config | Rollback all config from memory |
| rollback @@route | Rollback route config from memory |
| rollback @@user | Rollback user config from memory |
| show @@backend | Report backend connection status |
| show @@command | Report commands status |
| show @@connection | Report connection status |
| show @@connection.sql | Report connection sql |
| show @@database | Report databases |
| show @@datanode | Report dataNodes |
| show @@datanode where schema = ? | Report dataNodes |
| show @@datasource | Report dataSources |
| show @@datasource where dataNode = ? | Report dataSources |
| show @@heartbeat | Report heartbeat status |
| show @@parser | Report parser status |
| show @@processor | Report processor status |
| show @@router | Report router status |
| show @@server | Report server status |
| show @@slow where datanode = ? | Report datanode slow sql |
| show @@slow where schema = ? | Report schema slow sql |
| show @@sql where id = ? | Report specify SQL |
| show @@sql.detail where id = ? | Report execute detail status |
| show @@sql.execute | Report execute status |
| show @@sql.slow | Report slow SQL |
| show @@threadpool | Report threadPool status |
| show @@time.current | Report current timestamp |
| show @@time.startup | Report startup timestamp |
| show @@version | Report Mycat Server version |
| stop @@heartbeat name:time | Pause dataNode heartbeat |
| switch @@datasource name:index | Switch dataSource |
+--------------------------------------+-----------------------------------+
37 rows in set (0.02 secs)
2.2 常用監控命令
常用的監控命令如下所示:
2.2.1 reload @@config
在MyCAT的命令行監控窗口運行:
reload @@config;
該命令用于更新配置文件,例如更新schema.xml文件后在命令行窗口輸入該命令,可不用重啟即進行配置文件更新。運行結果參考如下:
mysql> reload @@config;
Query OK, 1 row affected (0.29 sec)
Reload config success
2.2.2 show @@database
在MyCAT的命令行監控窗口運行:
show @@database;
該命令用于顯示MyCAT的數據庫的列表,對應schema.xml配置文件的schema子節點,參考運行結果如下:
mysql> show @@database;
+------------+
| DATABASE |
+------------+
| photo |
| test_mycat |
| weixin |
| yixin |
+------------+
4 rows in set (0.00 sec)
2.2.3 show @@datanode
在MyCAT的命令行監控窗口運行:
show @@datanode;
該命令用于顯示MyCAT的數據節點的列表,對應schema.xml配置文件的dataNode節點,參考運行結果如下:
mysql> show @@datanode;
+--------+--------------------+-------+-------+--------+------+------+---------+------------+----------+---------+--------------+
|NAME|DATHOST|INDEX|TYPE|ACTIVE|IDLE|SIZE|EXECUTE|TOTAL_TIME|MAX_TIME|MAX_SQL|RECOVERY_TIME|
+--------+--------------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+
|photo|testhost/photo|0|mysql|0|7|1000|0|0|0|0|-1|
|user0|testhost/eip_user0|0|mysql|0|7|1000|5|0|0|0|-1|
|user1|testhost/eip_user1|0|mysql|0|7|1000|1|0|0|0|-1|
|user2|testhost/eip_user2|0|mysql|0|7|1000|1|0|0|0|-1|
|user3|testhost/eip_user3|0|mysql|0|7|1000|1|0|0|0|-1|
|weixin|testhost/weixin|0|mysql|0|7|1000|0|0|0|0|-1|
|yixin|testhost/yixin|0|mysql|0|7|1000|0|0|0|0|-1|
+--------+--------------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+
7 rows in set (0.05 sec)
其中,“NAME”表示dataNode的名稱;“dataHost”表示對應dataHost屬性的值,即數據主機;“ACTIVE”表示活躍連接數;“IDLE”表示閑置連接數;“SIZE”對應總連接數量。
運行如下命令,可查找對應的schema下面的dataNode列表:
show @@datanode where schema = ?
該命令的執行結果參考如下:
mysql> show @@datanode where schema = test_mycat;
+-------+--------------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+
|NAME|DATHOST|INDEX|TYPE|ACTIVE|IDLE|SIZE|EXECUTE|TOTAL_TIME|MAX_TIM| MAX_SQL|RECOVERY_TIME|
+-------+--------------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+
|user0|testhost/eip_user0|0|mysql|0|6|1000|5|0|0|0|-1|
|user1|testhost/eip_user1|0|mysql|0|6|1000|1|0|0|0|-1|
|user2|testhost/eip_user2|0|mysql|0|6|1000|1|0|0|0|-1|
|user3|testhost/eip_user3|0|mysql|0|6|1000|1|0|0|0|-1|
+-------+--------------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+
4 rows in set (0.00 sec)
2.2.4 show @@heartbeat
該命令用于報告心跳狀態,參考運行結果如下所示:
mysql> show @@heartbeat;
+--------+-------+--------------+------+---------+-------+--------+---------+----------------+---------------------+-------+
|NAME|TYPE|HOST|PORT|RS_CODE|RETRY|STATUS|TIMEOUT|EXECUTE_TIME|LAST_ACTIVE_TIME|STOP|
+--------+-------+--------------+------+---------+-------+--------+---------+----------------+---------------------+-------+
|hostM1|mysql|localhost|3306|1|0|idle|30000|0,2001,1445|2014-12-26 12:11:05|false|
|hostM3|mysql|localhost|3306|1|0|idle|30000|5003,5168,4278|2014-12-26 12:11:05|false|
|hostM2|mysql|10.18.96.144|3306|1|0|idle|30000|5,3,2|2014-12-26 12:11:05|false|
|hostM4|mysql|10.18.96.144|3306|1|0|idle|30000|2,2,2|2014-12-26 12:11:05|false|
+--------+-------+--------------+------+---------+-------+--------+---------+----------------+---------------------+-------+
4 rows in set (0.01 sec)
2.2.5 show @@version
該命令用于獲取MyCAT的版本,參考運行結果如下所示:
mysql> show @@version;
+------------------+
| VERSION |
+------------------+
| 5.1.48-mycat-1.2 |
+----------------+
1 row in set (0.00 sec)
2.2.6 show @@sql.slow
該命令用于查詢運行緩慢的SQL語句,參考運行結果如下所示:
mysql> show @@sql.slow;
Empty set (0.00 sec)
2.2.7 show @@connection
該命令用于獲取連接狀態,參考運行結果如下所示:
mysql> show @@connection;
+------------+------+-----------+------+------------+------------+---------+----
----+---------+---------------+-------------+------------+
|PROCESSOR|ID|HOST|PORT|LOCAL_PORT|SCHEMA|CHARSET|NET_IN|NET_OUT|ALIVE_TIME(S)|RECV_BUFFER|SEND_QUEUE|
+------------+------+-----------+------+------------+------------+---------+----
----+---------+---------------+-------------+------------+
|Processor0|5|127.0.0.1|8066|54448|test_mycat|utf8|320|44674|225|4096|0|
|Processor3|6|127.0.0.1|9066|54432|NULL|utf8|162|741|459|4096|0|
+------------+------+-----------+------+------------+------------+---------+----
----+---------+---------------+-------------+------------+
2 rows in set (0.04 sec)
3、 參考文檔
(1)《MyCAT inAction中文版》
(2)《Mycat命令行監控指南.docx》
posted on 2014-12-26 16:06
阿蜜果 閱讀(17488)
評論(2) 編輯 收藏 所屬分類:
database 、
架構師之路