Mysql的主从复制的读写分离之Amoeba实现

以前写过了一篇Mysql的主从复制,但没有实现Mysql的主从复制的读写分离。

关于读写分离:

  读写分离(Read/Write Splitting),基本的原理是让主数据库处理事务性增、改、删、操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。

关于Mysql的读写分离实现大致有三种:

1、 程序修改Mysql操作类

   就以程序来完成Mysql的读写操作,如以PHP程序、java程序等解决此需求。

优点:直接和数据库通信,简单快捷的读写分离和随机的方式实现的负载均衡,权限独立分配

缺点:自己维护更新,增减服务器上的代码处理。

2、 mysql-proxy

  MySQL-Proxy是处在你的MySQL数据库客户和服务端之间的程序,它还支持嵌入性脚本语言Lua。这个代理可以用来分析、监控和变换(transform)通信数据,它支持非常广泛的使用场景:

 

  • 负载平衡和故障转移处理
  • 查询分析和日志
  • SQL宏(SQL macros)
  • 查询重写(quer rewriting)
  • 执行shell命令

优点:直接实现读写分离和负载均衡,不用修改代码,master和slave用同一个账号

缺点:字符集问题,lua语言编程,还只是alpha版本,时间消耗有点高

3、 Amoeba

参考官网:http://amoeba,meidusa.com/ 
优点:直接实现读写分离和负载均衡,不用修改代码,有很灵活的数据解决方案
缺点:自己分配账户,和后端数据库权限管理独立,权限处理不够灵活

以上是三种常见的Mysql的读写分离方法。在这建议用第三种也就是amoeba来实现 

关于Amoeba

Amoeba(变形虫)项目,该开源框架于2008年开始发布一款Amoeba for Mysql软件。这个软件致力于Mysql的分布式数据库前端代理层,它主要在应用层访问Mysql的时候充当SQL路由功能,专注于分布式数据库代理层(Database Proxy)开发。位于Client、DB Server(s)之间,对客户透明。具有负载均衡、高可用性、SQL过滤、读写分离、可路由相关的到目标数据库、可并发请求多台数据库合并结果。通过Amoeba你能够完成多数据源的高可用、负载均衡、数据切片的功能,目前Amoeba已在很多企业的生产线上使用。

下面就来把 Mysql的主从复制和读写分离完整的实现一下。

先来做Mysql的主从复制。

我实现的环境如下:

System:Centos 5.4 32bit

主:192.168.1.107

从:192.168.1.139

读写分离:192.168.1.183

拓扑图如下:

 

Mysql的主从复制的读写分离之Amoeba实现_第1张图片

MySql的主从复制:

对主MysqL服务器,主要是开启二进制日志,这个默认是开启的,在配置文件中:

# vim /etc/my.cnf

server-id = 1 (默认为1)

log-bin = mysql-bin (这个也是默认开启的)

下面连接到mysql数据库中创建一个用户并赋予复制权限。

mysql> GRANT REPLICATION CLIENT,REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.139' IDENTIFIED BY "123456";

这样在主服务器上操作暂时完成了。

下面来在从服务器上操作:

修改主配置文件:my.cnf

   
   
   
   
  1. # vim /etc/my.cnf 
  2. 定位到 server-id = 1 
  3. 修改为:server-id = 2 
  4. 添加如下内容: 
  5. relayrelay-log = relay-bin 
  6. relayrelay-log-index = relay-bin.index 
  7. 修改完成后,保存退出; 
  8. # service mysqld restart 

因为在主mysql中已经有数据了,所以要把主服务器的数据导入到从服务器中,所以在主服务器上的操作:

 

   
   
   
   
  1. # mysqldump --all-databases --lock-all-tables --master-data=2 > /tmp/slave.sql  
  2. 导出后复制到从服务器上: 
  3. # scp /tmp/slave.sql 192.168.1.139:/tmp/ 
  4. 进入到从服务器上,导入数据。 
  5. # mysql < /tmp/slave.sql 

以上操作完成后就可以做把主从连接在一起了。

在从服务器上进入数据库中

   
   
   
   
  1. mysql> CHANGE MASTER TO MASTER_HOST='192.168.1.107',MASTER_USER='repl',MASTER_PASSWORD='123456'

 

下面就可以在从服务器上开启复制了

mysql> start slave;

 

查看是否连接成功:

   
   
   
   
  1. mysql> show slave status\G 
  2. 如下两行出现则表明主从复制成功: 
  3.  
  4. Slave_IO_Running: Yes 
  5. Slave_SQL_Running: Yes 

可在主服务器上创建一个数据库或表来在从服务器上来验证一下。

下面来进行最为核心的内容——读写分离

下面的操作要在读写分离的服务器上操作也就是:192.168.1.183

Amoeba的安装:

安装Amoeba前要先安装JDK,因为Amoeba是用java开发的所要有JDK支持。

   
   
   
   
  1. # java -version 
  2. java version "1.6.0_33" 
  3. Java(TM) SE Runtime Environment (build 1.6.0_33-b04) 
  4. Java HotSpot(TM) Server VM (build 20.8-b03, mixed mode) 

如果没有安装JDK,要先安装,方法如下 :

   
   
   
   
  1. 去oracle官网下载jdk安装包后,安装jdk  
  2. # chmod 755 jdk-6u33-linux-i586.bin  
  3. # ./jdk-6u33-linux-i586.bin  
  4. 默认安装到/usr/java/jdk1.6.0_33 
  5. 下面来修改环境变量: 
  6. # vim /etc/profile 
  7. 添加如下行: 
  8. export JAVA_HOME=/usr/java/jdk1.6.0_33 
  9. export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar 
  10. export PATH=$PATH:$JAVA_HOME/bin 
  11. 保存退出! 
  12. # source /etc/profile \\使之生效 
  13. 查看: 
  14. # java -version 
  15. java version "1.6.0_33" 
  16. Java(TM) SE Runtime Environment (build 1.6.0_33-b04) 
  17. Java HotSpot(TM) Server VM (build 20.8-b03, mixed mode) 

安装完成后就可以安装Amoeba了

下载:

   
   
   
   
  1. wget http://nchc.dl.sourceforge.net/project/amoeba/Amoeba%20for%20mysql/2.x/amoeba-mysql-binary-2.1.0-RC5.tar.gz 

安装:

   
   
   
   
  1. # mkdir /usr/local/amoeba 
  2. # mv amoeba-mysql-binary-2.1.0-RC5.tar.gz /usr/local/amoeba  
  3. # cd /usr/local/amoeba 
  4. # tar zxvf amoeba-mysql-binary-2.1.0-RC5.tar.gz 

解压后就完成了。下面要做的就是配置。

   
   
   
   
  1. # cd /usr/local/amoeba  \\主要是配置下面两个文件 
  2. dbServer.xml  \\ 定义连接数据库的信息
  3. amoeba.xml  \\ 定义读写分离的节点管理信息

下面就来配置一下。

# cd /usr/local/amoeba

# vim dbServer.xml

   
   
   
   
  1. xml version="1.0" encoding="gbk"?> 
  2.  
  3. > 
  4. <amoeba:dbServers xmlns:amoeba="http://amoeba.meidusa.com/"> 
  5.  
  6.                  
  7.                         <property name="port">3306property> 
  8.   \\这个是后端数据的端口
  9.                          
  10.                         <property name="schema">testproperty> 
  11.   \\这个是后端默认的数据库
  12.                          
  13.                         <property name="user">rootproperty> 
  14.  
  15.                          
  16.                         <property name="ipAddress">192.168.1.107property> 
  17.                         <property name="user">rootproperty>  \\连接数据库的用户名
  18.                         <property name="password">123456property>  \\连接数据库的密码
  19.                 factoryConfig> 
  20.         dbServer> 
  21.         <dbServer name="slave"  parent="abstractServer">  \\定义一个从节点
  22.                 <factoryConfig> 
  23.                          
  24.                         <property name="ipAddress">192.168.1.139property> 
  25.                         <property name="user">rootproperty> 
  26.                         <property name="password">123456property> 
  27.                 factoryConfig> 
  28.         dbServer> 
  29.   \\定义池,把master和slave加入
  30.         <dbServer name="server1" virtual="true">  \\server1是要把master节点加入
  31.                 <poolConfig class="com.meidusa.amoeba.server.MultipleServerPool"> 
  32.                            < !  -- 负载均衡参数1=ROUNDROBIN , 2=WEIGHTBASED , 3=HA-->
  33.                         <property name="loadbalance">1property> 
  34.  
  35.                          
  36.                         <property name="poolNames">masterproperty> 
  37.                 poolConfig> 
  38.         dbServer> 
  39.         <dbServer name="server2" virtual="true">  \\server2是要把slave节点加入
  40.                 <poolConfig class="com.meidusa.amoeba.server.MultipleServerPool"> 
  41.                          
  42.                         <property name="loadbalance">1property> 
  43.  
  44.                          
  45.                         <property name="poolNames">slaveproperty> 
  46.                 poolConfig> 
  47.         dbServer> 
  48.  
  49. amoeba:dbServers> 

下面来配置amoeba.xml文件

   
   
   
   
  1. xml version="1.0" encoding="gbk"?> 
  2.  
  3. > 
  4. <amoeba:configuration xmlns:amoeba="http://amoeba.meidusa.com/"> 
  5.  
  6.         <proxy> 
  7.  
  8.                  
  9.                 <service name="Amoeba for Mysql" class="com.meidusa.amoeba.net.ServerableConnectionManager"> 
  10.                          
  11.                         <property name="port">8066property> 
  12.   \\定义amoeba读写分离proxy对外代理的端口
  13.                          
  14.                          
  15.  
  16.                         <property name="manager">${clientConnectioneManager}property> 
  17.  
  18.                         <property name="connectionFactory"> 
  19.                                 <bean class="com.meidusa.amoeba.mysql.net.MysqlClientConnectionFactory"> 
  20.                                         <property name="sendBufferSize">128property> 
  21.                                         <property name="receiveBufferSize">64property> 
  22.                                 bean> 
  23.                         property> 
  24.  
  25.                         <property name="authenticator"> 
  26.                                 <bean class="com.meidusa.amoeba.mysql.server.MysqlClientAuthenticator"> 
  27.  
  28.                                         <property name="user">rootproperty> 
  29.   \\定义通过amoeba登录的用户名
  30.                                         <property name="password">123456property> 
  31.   \\相应的这个就是密码了。 定义proxy的管理帐号密码,客户端和程序只需要连接proxy的帐号密码即可,相当于中间接封装
  32.                                         <property name="filter"> 
  33.                                                 <bean class="com.meidusa.amoeba.server.IPAccessController"> 
  34.                                                         <property name="ipFile">${amoeba.home}/conf/access_list.confproperty> 
  35.                                                 bean> 
  36.                                         property> 
  37.                                 bean> 
  38.                         property> 
  39.  
  40.                 service> 
  41.  
  42.                  
  43.                 <service name="Amoeba Monitor Server" class="com.meidusa.amoeba.monitor.MonitorServer"> 
  44.                          
  45.                          
  46.                         <property name="ipAddress">127.0.0.1property> 
  47.                         <property name="daemon">trueproperty> 
  48.                         <property name="manager">${clientConnectioneManager}property> 
  49.                         <property name="connectionFactory"> 
  50.                                 <bean class="com.meidusa.amoeba.monitor.net.MonitorClientConnectionFactory">bean> 
  51.                         property> 
  52.  
  53.                 service> 
  54.  
  55.                 <runtime class="com.meidusa.amoeba.mysql.context.MysqlRuntimeContext"> 
  56.                          
  57.                         <property name="readThreadPoolSize">20property> 
  58.  
  59.                          
  60.                         <property name="clientSideThreadPoolSize">30property> 
  61.  
  62.                          
  63.                         <property name="serverSideThreadPoolSize">30property> 
  64.  
  65.                          
  66.                         <property name="statementCacheSize">500property> 
  67.  
  68.                          
  69.                         <property name="queryTimeout">60property> 
  70.                 runtime> 
  71.  
  72.         proxy> 
  73.  
  74.          
  75.         <dbServerLoader class="com.meidusa.amoeba.context.DBServerConfigFileLoader"> 
  76.                 <property name="configFile">${amoeba.home}/conf/dbServers.xmlproperty> 
  77.         dbServerLoader> 
  78.  
  79.         <queryRouter class="com.meidusa.amoeba.mysql.parser.MysqlQueryRouter"> 
  80.                 <property name="ruleLoader"> 
  81.                         <bean class="com.meidusa.amoeba.route.TableRuleFileLoader"> 
  82.                                 <property name="ruleFile">${amoeba.home}/conf/rule.xmlproperty> 
  83.                                 <property name="functionFile">${amoeba.home}/conf/ruleFunctionMap.xmlproperty> 
  84.                         bean> 
  85.                 property> 
  86.                 <property name="sqlFunctionFile">${amoeba.home}/conf/functionMap.xmlproperty> 
  87.                 <property name="LRUMapSize">1500property> 
  88.                 <property name="defaultPool">server1property> 
  89.  \\定义默认的池,一些除了SELECT\UPDATE\INSERT\DELETE的语句都会在defaultPool执行
  90.  
  91.                 <property name="writePool">server1property>
  92. \\定义写的池,这里的server1就是在dbServer.xml中的server1
  93.                 <property name="readPool">server2property> 
  94.   \\定义读的池,这里的server2就是在dbserver.xml中的server2
  95.                 <property name="needParse">trueproperty> 
  96.         queryRouter> 
  97. amoeba:configuration> 

配置完成后就可以启动amoeba了。

   
   
   
   
  1. # cd /usr/local/amoeba/bin 
  2. # ./amoeba  
  3. amoeba start|stop 
  4. # ./amoeba start &  \\会输出下面的信息 
  5. [1] 6789 
  6. log4j:WARN log4j config load completed from file:/usr/local/amoeba/conf/log4j.xml 
  7. 2012-09-06 17:56:01,619 INFO  context.MysqlRuntimeContext - Amoeba for Mysql current versoin=5.1.45-mysql-amoeba-proxy-2.1.0-RC5 
  8. log4j:WARN ip access config load completed from file:/usr/local/amoeba/conf/access_list.conf 
  9. 2012-09-06 17:56:01,987 INFO  net.ServerableConnectionManager - Amoeba for Mysql listening on 0.0.0.0/0.0.0.0:8066.  
  10. 2012-09-06 17:56:01,992 INFO  net.ServerableConnectionManager - Amoeba Monitor Server listening on /127.0.0.1:6376. 
  11. 从上面的信息中可以看到8066端口打开了。可查看进程: 
  12. # ps aux | grep amoeba 
  13. root   6789  4.1  1.5 408940 31908 pts/1    Sl   17:56   0:00 /usr/java/jdk1.6.0_33/bin/java -server -Xms256m -Xmx256m -Xss12sworlds.conf=/usr/local/amoeba/bin/amoeba.classworlds -classpath /usr/local/amoeba/lib/classworlds-1.0.jar org.co 
  14. root   6820  0.0  0.0   5112   680 pts/1    S+   17:56   0:00 grep amoeba 
  15. # netstat -tlnp 
  16. Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name    
  17. tcp        0      0 0.0.0.0:111                 0.0.0.0:*                   LISTEN      -                    
  18. tcp        0      0 0.0.0.0:688                 0.0.0.0:*                   LISTEN      -                    
  19. tcp        0      0 127.0.0.1:631               0.0.0.0:*                   LISTEN      -                    
  20. tcp        0      0 127.0.0.1:25                0.0.0.0:*                   LISTEN      -                    
  21. tcp        0      0 :::8066                     :::*                        LISTEN      6789/java            
  22. tcp        0      0 ::ffff:127.0.0.1:6376       :::*                        LISTEN      6789/java            
  23. tcp        0      0 :::22                       :::*                        LISTEN      -              

从上面的一些信息可以看到amoeba已经启动,下面就可以测试一下了。

下面来做一测试:

为了更好的看到测试结果,要做以下这些步骤:

测试之前要先保证amoeba-server有访问两个主从服务器test库的权限,在主mysql上执行:

   
   
   
   
  1. mysql> grant all privileges on *.* to 'root'@'192.168.1.183' identified by "123456"; 
  2. mysql> flush privileges; 
  3. 用户创建完成后就可以登录了 
  4. # mysql -uroot -p123456 -h192.168.1.183 -P8066 

下面开始测试:

先让主从开始复制,即在从服务器上执行:

mysql> slave start;

再从读写分离的服务器上登录:

# mysql -uroot -p123456 -h192.168.1.183 -P8066

进入之后,先来创建一个数据库zhou,然后在这个数据库中创建一个表test 

mysql> create database zhou;

mysql> use zhou;

mysql> create table test (id int(10), name varchar(20),adress varchar(30));

做完这些,回到从服务器上执行:

mysql> slave stop;

接着在主从服务器上各加入一条不同的数据。

在主上:

mysql> use zhou;

mysql> insert into test ('1','zhou','this_is_master');

在从上:

mysql> use zhou;

mysql> insert into test ('2','zhou','this_is_slave');

完成后就可以在读写分离服务器上测试读写了

在读写分离服务器上:

mysql> use zhou;

 

mysql> select * from test;

+------+------+---------------+

| id   | name | address       |

+------+------+---------------+

|    2 | zhou | this_is_slave | 

+------+------+---------------+

1 row in set (0.01 sec)

从结果可以看出数据是读的从服务器上的数据,然后我们直接插入数据,再来测试

 

mysql> insert into test values('3','hhh','test_write');

Query OK, 1 row affected (0.01 sec)

mysql> select * from test;

+------+------+---------------+

| id   | name | address       |

+------+------+---------------+

|    2 | zhou | this_is_slave | 

+------+------+---------------+

1 row in set (0.00 sec)

 

结果显示出的数据没有改变,因为我们把主从复制停了,所以数据没有同步,从查询的结果可以看到,数据还是来自从服务器。

然后们再在主服务器上查询可以看到:

 

mysql> select * from test;

+------+------+--------------- +

| id   | name | address        |

+------+------+--------------- +

|    1 | zhou | this_is_master | 

+------+------+--------------- +

|    3 | hhh  |  test_write    | 

+------+------+--------------- +

 

 

从上面的结果可以看出,数据的读写分离成功了。

以上就是mysql数据库的主从复制以及读写分离的整个过程。文中可能还存在不完整或者出错的地方,还请大家指出来,谢谢了。