mycat讀寫分離+垂直切分+水平切分+er分片+全局表 測試
原文http://blog.163.com/[email protected]/blog/static/172718064201683031639683/
讀寫分離:利用最基礎的mysql主從復制,事務性的查詢無法分離出去(因為會導致數據不一致),這樣就無法做到真正的讀寫分離,因為有些場景可能大部分都是事物性的讀。解決方法: galera for mysql 強一致性。 http://www.blogjava.net/amigoxie/archive/2014/12/24/421788.html
http://blog.csdn.net/benluobobo/article/details/51099607
http://blog.csdn.net/wulex/article/details/52495488 好的實例連接:http://blog.csdn.net/wulex/article/details/52495488
在user0~user2創建同樣的表結構,t_user和t_user_class_rel的建表語句參考如下:
CREATE TABLE `t_user_ext` (
`user_id` int(11) NOT NULL COMMENT ‘用戶ID‘,
`receive_address` varchar(256) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT ‘收貨地址‘,
`create_time` datetime NOT NULL,
`province_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`user_id`)
DROP TABLE IF EXISTS `t_user_class_rel`;
CREATE TABLE `t_user_class_rel` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT ‘id‘,
`caller` varchar(16) CHARACTER SET utf8 NOT NULL COMMENT ‘調用方系統表示‘,
`province_code` varchar(10) CHARACTER SET utf8 DEFAULT NULL COMMENT ‘省份編碼‘,
`user_id` int(11) NOT NULL COMMENT ‘用戶ID‘,
`class_id` int(11) NOT NULL COMMENT ‘班級ID‘,
`role_type` int(11) DEFAULT NULL COMMENT ‘用戶在該班的角色(1學生2家長3教師)‘,
`create_time` datetime NOT NULL COMMENT ‘創建時間‘,
`modify_time` datetime DEFAULT NULL COMMENT ‘修改時間‘,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_rel_user_class_id` (`user_id`,`class_id`,`role_type`),
KEY `idx_rel_user_id` (`user_id`) USING BTREE,
KEY `idx_rel_class_id` (`class_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 3、添加schema:加了一點內容:不分表的情況測試(只對部分表進行切分。其實這種時候,沒有切分的表,應該是不需要跟已經切分過的表進行關聯,否則就會垮庫join。既然是這樣,那業務就比較獨立了,為什麽不把這部分表垂直切分出去呢?) 總結心得:1、如果某張表進行水平切分了,那麽跟他有事物關聯的表,要麽搞全局表,要麽進行er分片,不然就會導致垮庫join。而沒有關聯關系的表或者非事物關聯的表,實際上可以垂直切分出去(如果有必要)。2、dataHost可以理解成一個主機組,可以是單機,可以是主從,可以是galera 等搭建起來的集群。讀寫分離就是在這裏處理的。ha、讀寫分離等都在這裏進行配置,都是針對datahost。 <schema name="test_sharding" checkSQLschema="false" sqlMaxLimit="100">
<!-- auto sharding by id (long) -->
<table name="t_user" dataNode="user0,user1,user2,user3" rule="rule_wyh">
<childTable name="t_user_class_rel" primaryKey="id" joinKey="user_id" parentKey="user_id" />
</table>
</table>
</schema> <dataNode name="user0" dataHost="host0" database="user0" />
<dataNode name="user1" dataHost="host1" database="user1" />
<dataNode name="user2" dataHost="host2" database="user2" />
<dataNode name="user3" dataHost="host3" database="user3" />
4、添加datahost:host3 <dataHost name="host3" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native"> <heartbeat>select user()</heartbeat> <!-- can have multi write hosts --> <writeHost host="ys-fs" url="192.168.168.238:3306" user="root" password="youngsun" /> </dataHost> 在238上授權授權: GRANT ALL PRIVILEGES ON *.* TO ‘root‘@‘%‘ IDENTIFIED BY ‘youngsun‘; flush privileges; 5、配置rule.xml文件
在schema.xml的文件內容中可看到t_user表指定的分片規則是rule1,需要在conf/rule.xml文件中設置rule1的規則為根據user_id進行分片,並按照類“org.opencloudb.route.function.PartitionByLong”的規則進行分片,即將user_id模除1024後每256內分到一個數據庫中,即模除後0~255到user0數據庫庫,256~511到user1數據庫,512~767到user2數據庫,768~1023到user3數據庫。
總結心得:普通取模算法,連續的id會路由到不同的分片。增大了批量插入的事務控制難度,而固定分片hash算法根據二進制則可能會分到連續的分片,減少插入事務事務控制難度。
該文件的參考內容如下所示:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://org.opencloudb/">
<tableRule name="rule_wyh">
<rule>
<columns>user_id</columns>
<algorithm>func_4p</algorithm>
</rule>
</tableRule>
<function name="func_4p" class="org.opencloudb.route.function.PartitionByLong">
<property name="partitionCount">4</property>
<property name="partitionLength">256</property>
</function>
</mycat:rule> 6、配置server.xml文件
在server.xml文件中的schemas屬性中添加test_sharding的schema。修改後的文件如下所示:
<!DOCTYPE mycat:server SYSTEM "server.dtd"><mycat:server xmlns:mycat="http://org.opencloudb/">
<system>
<property name="sequnceHandlerType">0</property>
</system>
<user name="test">
<property name="password">test</property>
<property name="schemas">weixin,yixin,photo,test_sharding</property>
</user>
</mycat:server> 7、水平切分測試
重啟MyCAT,使用MySQL客戶端連接後,連接後可在test_sharding數據庫下看到t_user和t_user_class_rel表,
在MySQL客戶端連接的MyCat的test_sharding數據庫的t_user表運行如下插入語句,插入user_id=1、255、256、511、512、1023、1024、50、300、1000的數據:註意insert into 必須帶上字段名列表,不然報錯插不進去。
INSERT INTO t_user( user_id , receive_address , create_time , province_code ) VALUES(‘1‘, ‘廣州市越秀區廣州大道中599號‘, ‘2014-07-17 10:53:15‘, ‘GD‘); INSERT INTO t_user( user_id , receive_address , create_time , province_code ) VALUES(‘255‘, ‘廣州市越秀區廣州大道中599號‘, ‘2014-07-17 10:53:15‘, ‘GD‘); INSERT INTO t_user( user_id , receive_address , create_time , province_code ) VALUES(‘256‘, ‘廣州市越秀區廣州大道中599號‘, ‘2014-07-17 10:53:15‘, ‘GD‘); INSERT INTO t_user( user_id , receive_address , create_time , province_code ) VALUES(‘511‘, ‘廣州市越秀區廣州大道中599號‘, ‘2014-07-17 10:53:15‘, ‘GD‘); INSERT INTO t_user( user_id , receive_address , create_time , province_code ) VALUES(‘512‘, ‘廣州市越秀區廣州大道中599號‘, ‘2014-07-17 10:53:15‘, ‘GD‘); INSERT INTO t_user( user_id , receive_address , create_time , province_code ) VALUES(‘1023‘, ‘廣州市越秀區廣州大道中599號‘, ‘2014-07-17 10:53:15‘, ‘GD‘); INSERT INTO t_user( user_id , receive_address , create_time , province_code ) VALUES(‘1024‘, ‘廣州市越秀區廣州大道中599號‘, ‘2014-07-17 10:53:15‘, ‘GD‘); INSERT INTO t_user( user_id , receive_address , create_time , province_code ) VALUES(‘50‘, ‘廣州市越秀區廣州大道中599號‘, ‘2014-07-17 10:53:15‘, ‘GD‘); INSERT INTO t_user( user_id , receive_address , create_time , province_code ) VALUES(‘300‘, ‘廣州市越秀區廣州大道中599號‘, ‘2014-07-17 10:53:15‘, ‘GD‘); INSERT INTO t_user( user_id , receive_address , create_time , province_code ) VALUES(‘1000‘, ‘廣州市越秀區廣州大道中599號‘, ‘2014-07-17 10:53:15‘, ‘GD‘);而後在MyCAT的test_sharding數據庫的t_user表運行select查看記錄執行情況。進入localhost的user0~user3數據庫,查看數據是否按照之前確定的rule1的規則寫入不同的數據庫。
讀者可在test_sharding數據庫的t_user表執行update和delete等語句,並去分庫查看執行結果,可得知MyCAT對MySQL客戶端基本透明,對程序也幾乎透明,在select語句運行時,MyCAT會自行去各個分庫按照規則獲取合並結果。
接著測試按照ER關系策略分片的t_user_class_rel表是否按照user_id的分片策略,同樣user_id的數據分布在同一個user庫的t_user表和t_user_class_rel表。
在MyCAT的test_mycat數據庫的t_user_class_rel表運行如下語句:
INSERT INTO `t_user_class_rel`( `id` , `caller` , `province_code` , `user_id` , `class_id` , `role_type` , `create_time` , `modify_time`) VALUES (‘257‘, ‘eip‘, ‘GD‘, ‘2‘, ‘35‘, ‘3‘, ‘2012-08-05 17:32:13‘, ‘2013-12-27 16:07:32‘); INSERT INTO `t_user_class_rel`( `id` , `caller` , `province_code` , `user_id` , `class_id` , `role_type` , `create_time` , `modify_time`) VALUES (‘1‘, ‘eip‘, ‘GD‘, ‘257‘, ‘35‘, ‘3‘, ‘2012-08-05 17:32:13‘, ‘2013-12-27 16:07:32‘); INSERT INTO `t_user_class_rel`( `id` , `caller` , `province_code` , `user_id` , `class_id` , `role_type` , `create_time` , `modify_time`) VALUES (‘2‘, ‘eip‘, ‘GD‘, ‘513‘, ‘35‘, ‘3‘, ‘2012-08-05 17:32:13‘, ‘2013-12-27 16:07:32‘); INSERT INTO `t_user_class_rel`( `id` , `caller` , `province_code` , `user_id` , `class_id` , `role_type` , `create_time` , `modify_time`) VALUES (‘3‘, ‘eip‘, ‘GD‘, ‘769‘, ‘35‘, ‘3‘, ‘2012-08-05 17:32:13‘, ‘2013-12-27 16:07:32‘);而後在MyCAT的test_mycat數據庫的t_user_class_rel表運行select查看記錄執行情況。進入localhost的user0~user3數據庫,查看數據是否按照之前確定的rule1的規則和ER分片策略寫入不同的數據庫。
分片join解決方案心得小結:如果一張表做分片了,其他有一張表要跟這張表做關聯,方案如下:
1、全局表(適合做的才做):非跨分片join
2、另一張表也搞分片:非跨分片join
3、share join(只能2個表join):跨分片join
4、另一張表裏join用到的字段冗余到 已經做了分片的那張表上去:不用join (該方案可用性不錯)
5、另一張表裏join用到的字段 搞成一張全局表:非跨分片join
三、讀寫分離
MyCAT的讀寫分離機制如下:
- 事務內的SQL,全部走寫節點,除非某個select語句以註釋/*balance*/開頭
- 自動提交的select語句會走讀節點,並在所有可用讀節點中間隨機負載均衡
- 當某個主節點宕機,則其全部讀節點都不再被使用,因為此時,同步失敗,數據已經不是最新的,MyCAT會采用另外一個主節點所對應的全部讀節點來實現select負載均衡。
- 當所有主節點都失敗,則為了系統高可用性,自動提交的所有select語句仍將提交到全部存活的讀節點上執行,此時系統的很多頁面還是能出來數據,只是用戶修改或提交會失敗。
mycat讀寫分離+垂直切分+水平切分+er分片+全局表 測試