原文http://blog.163.com/bigoceanwu@126/blog/static/172718064201683031639683/
讀寫分離:利用最基礎的mysql主從復制,事務性的查詢無法分離出去(因為會導致數據不一致),這樣就無法做到真正的讀寫分離,因為有些場景可能大部分都是事物性的讀。解決方法: galera for mysql 強一致性。
http://blog.csdn.net/benluobobo/article/details/51099607
安裝使用過程遇到的問題:
1、mycat啟動后報錯,進程直接退出: Error: Exception thrown by the agent : java.net.MalformedURLException: Local host name unknown: java.net.UnknownHostException: ys-fs: ys-fs: Name or service not known
原因:本機要配置/etc/hosts 127.0.0.1 主機名
一、垂直切分測試:
1、schema.xml里面加入:
<schema name="weixin" checkSQLschema="false" sqlMaxLimit="100" dataNode="weixin" />
<schema name="yixin" checkSQLschema="false" sqlMaxLimit="100" dataNode="yixin" />
<schema name="sms" checkSQLschema="false" sqlMaxLimit="100" dataNode="sms" />
<dataNode name="weixin" dataHost="host0" database="weixin" />
<dataNode name="yixin" dataHost="host1" database="yixin" />
<dataNode name="sms" dataHost="host2" database="sms" />
<dataHost name="host0" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="namenode" url="192.168.168.230:3306" user="root" password="youngsun" />
</dataHost>
<dataHost name="host1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="hadoop1" url="192.168.168.231:3306" user="root" password="youngsun" />
</dataHost>
<dataHost name="host2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="hadoop2" url="192.168.168.232:3306" user="root" password="youngsun" />
</dataHost>
2、server.xml加入:
<user name="test_wyh">
<property name="password">test</property>
<property name="schemas">weixin,yixin,sms</property>
</user>
3、遇到問題:
1)、Caused by: org.xml.sax.SAXParseException; lineNumber: 106; columnNumber: 16; The content of element type "mycat:schema" must match "(schema*,dataNode*,dataHost*)".
原因:要按照schema、datanode 、datahost的順序放,不能打亂。也就是所有schema要放一起,然后接着才能放datanode。。。。
2)、報1184錯誤,是因為沒有 把datahost主機的權限授予mycat所在主機。
<writeHost host="hadoop2" url="192.168.168.232:3306" user="root" password="youngsun" />
這里的 用戶要授予mycat所在主機遠程訪問權限:
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'youngsun'
二、水平切分測試:
1、分別建立4個庫:user0、user1、user2、user3。我這里4個庫建在4個獨立的主機上。
CREATE DATABASE user0 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
2、創建表結構
在user0~user2創建同樣的表結構,t_user和t_user_class_rel的建表語句參考如下:

DROP TABLE IF EXISTS `t_user_ext`;

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`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='用戶信息表';

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>
<!--
此處測試不分表的情況。要先在這里配置,然后可以在mycat創建t_user_1表,也可以在user3對應的local創建表。如果這里沒事先配置,無法在mycat建表,會報錯。這個還可以通過制定默認datanode實現,更簡單,配置方法:在shcema標簽上加上datanode -->
<table name="t_user_1" dataNode="user3" >

</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語句仍將提交到全部存活的讀節點上執行,此時系統的很多頁面還是能出來數據,只是用戶修改或提交會失敗。
231和233主從配置,233配置成讀庫。
<dataHost name="host1" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="hadoop1" url="192.168.168.231:3306" user="root" password="youngsun" >
<readHost host="hadoop3" url="192.168.168.233:3306" user="root" password="youngsun" weight="1" />
</writeHost>
</dataHost>