一、背景知识
之前已经写了一篇关于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主从读写分离(比较多的测试)