一、背景知識
之前已經寫了一篇關於Amoeba讀寫分離:Ubuntu10下MySQL搭建Amoeba_讀寫分離,上篇文章只是簡單的測試下搭建讀寫分離,這里再加上一點的內容,在讀寫分離的基礎上再加入分片。
閱讀本篇文章之前建議你先閱讀:
測試內容:把數據分片到Master1與Master2,再由Master1同步到Slave1,Master2同步到Slave2,把對數據的讀取分布到Slave1和Slave2。
二、搭建過程
(一) 測試環境
Amoeba for MySQL:192.168.1.147
Master1:192.168.1.25
Slave1:192.168.1.30
Master2:192.168.1.35
Slave2:192.168.1.40
數據庫為:dba_db,帳號密碼統一為:test/123456,GRANT ALL ON *.* TO test@'%' IDENTIFIED BY '123456';
(二) 前期准備
1. 驗證Amoeba是否安裝成功的命令(如下圖):/usr/local/amoeba/bin/amoeba
![]()
(Figure1:安裝成功)
2. 啟動amoeba:/usr/local/amoeba/bin/amoeba start

(Figure 2:啟動成功)
(三) Amoeba讀寫分離+水平切片配置
1. 修改amoeba.xml,設置登陸amoeba的帳號密碼。
<property name="user">testuser</property>
<property name="password">password</property>
2. 測試使用上面帳號是否正常登陸,如果出現上面的圖2界面說明設置的帳號密碼成功。
#mysql -u testuser -p -h 192.168.1.147 -P 8066
3. 修改dbServers.xml,設置數據庫、登陸MySQL的帳號和密碼。
<property name="schema">dba_db</property>
<property name="user">test</property>
<property name="password">123456</property>
4. 修改dbServers.xml,設置數據庫服務器的IP地址和服務器別名。
<dbServer name="Master1" parent="abstractServer">
<factoryConfig>
<property name="ipAddress">192.168.1.25</property>
</factoryConfig>
</dbServer>
<dbServer name="Slave1" parent="abstractServer">
<factoryConfig>
<property name="ipAddress">192.168.1.30</property>
</factoryConfig>
</dbServer>
<dbServer name="Master2" parent="abstractServer">
<factoryConfig>
<property name="ipAddress">192.168.1.35</property>
</factoryConfig>
</dbServer>
<dbServer name="Slave2" parent="abstractServer">
<factoryConfig>
<property name="ipAddress">192.168.1.40</property>
</factoryConfig>
</dbServer>
修改dbServers.xml,設置ROUNDROBIN(輪詢策略);(省略或者注釋)
<dbServer name="virtualSlave" virtual="true">
<poolConfig class="com.meidusa.amoeba.server.MultipleServerPool">
<!-- Load balancing strategy: 1=ROUNDROBIN , 2=WEIGHTBASED , 3=HA-->
<property name="loadbalance">1</property>
<property name="poolNames">Slave1,Slave2,Slave2</property>
</poolConfig>
</dbServer>
修改amoeba.xml,設置讀寫分離,修改queryRouter標簽下的;(省略或者注釋)
<property name="LRUMapSize">1500</property>
<property name="defaultPool">Master</property>
<property name="writePool">Master</property>
<property name="readPool">virtualSlave</property>
<property name="needParse">true</property>
5. 修改rule.xml,添加對label表的讀寫分離+水平切片規則;
<tableRule name="label" schema="dba_db" defaultPools="Master1,Slave1,Master2,Slave2" writePools="Master1,Master2" readPools="Slave1,Slave2">
<rule name="rule1" ruleResult="POOLNAME">
<parameters>ID</parameters>
<expression><![CDATA[
var hashid = abs(hash(ID)) % 2;
case hashid when 0 then (isReadStatement?'Slave1':'Master1');
when 1 then (isReadStatement?'Slave2':'Master2');
end case;
]]></expression>
</rule>
</tableRule>
這個規則的意思是:把數據取模切片到Master1與Master2,再由Master1同步到Slave1,Master2同步到Slave2,對數據的讀取分布到Slave1和Slave2。isReadStatement?'Slave1':'Master1'判斷是否是讀,如果是,分到Slave1,如果不是分到Master1。
6. 重新啟動amoeba,如果出現上面的圖2界面說明設置成功;
(四) MySQL的Master/Slave配置
1. Master1服務器/etc/mysql/my.cnf目錄 [mysqld] 區塊中加上:
log-bin=mysql-bin
server-id=1
innodb_flush_log_at_trx_commit=1
sync_binlog=1
binlog_do_db=dba_db
binlog_ignore_db=mysql
2. 登陸Master1的MySQL,新建一個用戶賦予“REPLICATION SLAVE”的權限。你不需要再賦予其它的權限:(因為是有兩個Salve,所以使用了%)
mysql>use mysql;
mysql>create user viajar@'192.168.1.%' identified by '123456';
mysql>grant replication slave on *.* to viajar@'192.168.1.%' identified by '123456';
3. 重啟Master的MySQL:
#service mysql restart
4. 獲取Master1的binlog信息:(mysql-bin.000136,106)
mysql>SHOW MASTER logs;

(Figure 3:Master信息)
5. Slave1服務器/etc/mysql/my.cnf目錄 [mysqld] 區塊中加上:
server-id=2
6. 重啟Slave1的MySQL:
#service mysql restart
7. 登陸Slave1的MySQL,執行命令:
mysql> change master to master_host='192.168.1.25', master_user='viajar', master_password='123456', master_log_file='mysql-bin.000136', master_log_pos=106;
8. 啟動Slave1的Slave:
mysql>START SLAVE;
9. 檢查Slave1的Slave是否正常:
mysql>show slave status\G;
10. 以同樣的方式配置Master2、Slave2,第5步與第7步會有些差異。
(五) 驗證讀寫分離+水平切片
1. 登陸到amoeba(147),插入下面的測試數據
INSERT INTO label(Id,NAME,Description,AddOn)
VALUES (1,'viajar','我的博客',NOW());
INSERT INTO label(Id,NAME,Description,AddOn)
VALUES (2,'viajar','我的博客',NOW());
INSERT INTO label(Id,NAME,Description,AddOn)
VALUES (3,'viajar','我的博客',NOW());
INSERT INTO label(Id,NAME,Description,AddOn)
VALUES (4,'viajar','我的博客',NOW());
2. 登陸Master1(25)、Slave1(30)分別驗證數據的分布,看到數據都如下圖所示:
mysql> SELECT * FROM label;

(Figure 4:Master1、Slave1)
3. 登陸Master2(35)、Slave2(40)分別驗證數據的分布,看到數據都如下圖所示:
mysql> SELECT * FROM label;

(Figure 5:Master2、Slave2)
圖4與圖5已經證明了數據進行了水平切片。
為了更明顯的測試讀寫分離,我們修改幾台服務器上的值:
master1:mysql>UPDATE label SET NAME = 'master1';
slave1:mysql>UPDATE label SET NAME = 'slave1';
master2:mysql>UPDATE label SET NAME = 'master2';
slave2:mysql>UPDATE label SET NAME = 'slave2';
4. 登陸Amoeba(147)驗證數據的分布,看到數據都如下圖所示:
mysql> SELECT * FROM label;

(Figure 6:Amoeba)
5. 登陸Amoeba(147)驗證數據的分布,看到數據都如下圖所示:
mysql> SELECT * FROM label WHERE id =1;
![]()
(Figure 7:Amoeba)
圖4的意思是搭建從Master1到Slave1的replication成功了;
圖5的意思是搭建從Master2到Slave2的replication成功了;
圖6可以看到查詢的都是從服務器上的數據;
圖7的意思是在一定程度上達到了讀寫分離的效果了,Master2與Slave2同樣有ID=1的記錄,但是只返回了一條記錄。
我們再進一步驗證Amoeba讀取到的這條記錄是Master2還是Slave2的數據:
6. 停止Slave2上的Slave,刪除id為1的記錄,剩余圖8中顯示的數據:
mysql>stop slave;
mysql>DELETE FROM label WHERE id =1;
![]()
(Figure 8:Slave2)
7. 到Amoeba上執行下面查詢,查詢返回的結果集:
mysql>SELECT * FROM label WHERE id =1;
![]()
(Figure 9:結果集為空)
到此Amoeba的讀寫分離+水平切片已經測試完畢。
三、注意事項
1. 在做Master/Slave的時候,我們有兩台Salve,而且是內網的機器,所以使用了“192.168.1.%”這樣的方式來創建用戶,在一定的程度上保障帳號的安全性,又方便管理,使用“*”是錯誤的;
2. 對於多Slave的帳號問題,還可以有其它方式來創建帳號的,就是在Master為每個Slave創建獨立的帳號和密碼;
3. 修改log4j.xml 取消日志文件生成(太大了,磁盤很容易滿),<param name="file" value="${amoeba.home}/logs/project.log"/>改成:<param name="file" value="<![CDATA[${amoeba.home}/logs/project.log>/dev/null]]>"/>
4. 性能優化,打開bin/amoeba,DEFAULT_OPTS="-server -Xms256m -Xmx256m -Xss128k"改成:DEFAULT_OPTS="-server -Xms512m -Xmx512m -Xmn100m -Xss1204k"Amoeba新版本MYSQL讀寫分離配置
5. 當tableRule中沒有任何規則能命中該表,將操作tableRule配置的writePools(若沒有配置則使用defaultPools)屬性中的數據庫節點;Amoeba切分規則配置詳解
6. 當某個表在任何tableRules都沒有被路由到,將操作queryRouter配置的writePools(若沒有配置則使用defaultPools)屬性中的數據庫節點。
7. rule的屬性group表示規則的組名,若多個rule元素有相同的組名,則一旦命中不再驗證后面的規則。
8. rule的屬性ruleResult有兩個選項:POOLNAME和BOOLEAN。配置為POOLNAME則expression返回數據庫節點名;配置為BOOLEAN則當expreesion返回true是操作writePools(若沒有配置writePools則操作defaultPools)
9. defaultPools、readPools、writePools均是一個或多個數據庫節點名以逗號為分隔符連接的字符串。數據庫節點名必須在dbServers.xml定義。需要切分的表名可以使用正則表達式,但是不允許用“.”號,因為目前“.”號作為schema與table的分割符號。 比如如果需要通配以“blog”開頭的任意表名,需要寫成:“blog[A-Za-z0-9]*”。
10. 所有的切片規則都一樣,首先要知道切分規則並且要命中,所以要首先生成ID值,在插入的時候指明ID值;
四、疑問解答
1. 在Amoeba中執行mysql> SELECT * FROM label; 有可能會返回下圖的結果集,造成這樣的原因是在rule.xml的匹配過程中沒有符合規則的(查詢中沒有帶ID),所以它到每個數據庫都執行了一次全表查詢,再把數據記錄合並起來;
應該如何防止這些情況的出現呢?如何在rule.xml的匹配規則中防止全表的查詢?還有沒類似全表查詢這樣類似的情況呢?

(Figure 10:結果集)
在rule.xml的tableRule標簽中沒有設置writePools="Master1,Master2" readPools="Slave1,Slave2"
2. rule.xml中使用的函數能自定義嘛?ruleFunctionMap.xml是如何使用的?類似於我能不能把對UUID做hash,再對key值取模等這些規則放到配置中去呢?自定義的函數。amoeba-mysql-binary-2.1.0-RC5.tar.gz暫時還沒有相關的文檔:Amoeba:分布式數據庫Proxy解決方案
五、參考文獻
Amoeba切分規則配置詳解(規則比較有含量)
Amoeba新版本MYSQL讀寫分離配置(log4j.xml設置)
Amoeba for mysql讀寫分離(比較多的測試)
Amoeba搞定mysql主從讀寫分離(比較多的測試)
