关于读写分离:
读写分离(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(变形虫)项目,专注 分布式数据库 proxy 开发。座落与Client、DB Server(s)之间。对客户端透明。具有负载均衡、高可用性、sql过滤、读写分离、可路由相关的query到目标数据库、可并发请求多台数据库合并结果。
主要解决:
降低 数据切分带来的复杂多数据库结构
提供切分规则并降低 数据切分规则 给应用带来的影响
降低db 与客户端的连接数
读写分离
二、为什么要用Amoeba
目前要实现mysql的主从读写分离,主要有以下几种方案:
1、 通过程序实现,网上很多现成的代码,比较复杂,如果添加从服务器要更改多台服务器的代码。
2、 通过mysql-proxy来实现,由于mysql-proxy的主从读写分离是通过lua脚本来实现,目前lua的脚本的开发跟不上节奏,而写没有完美的现成的脚本,因此导致用于生产环境的话风险比较大,据网上很多人说mysql-proxy的性能不高。
3、 自己开发接口实现,这种方案门槛高,开发成本高,不是一般的小公司能承担得起。
4、 利用阿里巴巴的开源项目Amoeba来实现,具有负载均衡、高可用性、sql过滤、读写分离、可路由相关的query到目标数据库,并且安装配置非常简单。国产的开源软件,应该支持,目前正在使用,不发表太多结论,一切等测试完再发表结论吧,哈哈!
三、快速架设amoeba,实现mysql主从读写分离
假设amoeba的前提条件:
n Java SE 1.5 或以上 Amoeba 框架是基于JDK1.5开发的,采用了JDK1.5的特性。
n 支持Mysql 协议版本10(mysql 4.1以后的版本)。
n 您的网络环境至少运行有一个mysql 4.1以上的服务
1、首先介绍下我的实验环境。
System: CentOS release 6.7
Master mysql:172.16.1.52
Slave mysql:172.16.1.51
Amoeba server: 10.0.0.5
安装数据库主从环境实现主从同步:
1.数据库安装(略)
2.主从同步
主库操作:
mysql -uroot -p123456 -S /data/3306/mysql.sock#登录数据库 mysql> grant replication slave on *.* to 'rep'@'172.16.1.%' identified by '123456'; #授权 mysql> flush privileges; #刷新权限 mysql> select user,host from mysql.user; #查看用户 mysql> show grants for ; #查看授权 mysql> flush table with read lock; #锁表 需要授权一个用户备用: mysql> grant select,insert,update,delete on ABC.* to zy@'172.16.1.%' identified by '123456'; mysql> quit #退出
打包备份同步到从库:
mysqldump -uroot -p123456 -S /data/3306/mysql.sock -B -F -R -x --master-data=2 -A --events|gzip >/server/backup/rep3306_$(date +%F).sql.gz #打包 gzip -d rep3308_2015-12-27.sql.gz #解压 mysql -uroot -p123456 -h172.16.1.51 -P3306< rep3308_2015-12-27.sql#同步到从库
从库操作:
mysql -uroot -p123456#登录 #执行 CHANGE MASTER TO MASTER_HOST='172.16.1.52', MASTER_PORT=3306, MASTER_USER='root', MASTER_PASSWORD='123456'; =================================== mysql> start slave; mysql> show slave status\G 注意:如果出现不必要的错误可以跳过,执行命令: mysql> stop slave; mysql> set global sql_slave_skip_counter =1; mysql> start slave; mysql> show slave status\G
测试(略)
提示:任意创建数据库表插入数据查看主从是否同步,到此为止主从同步完成!
Amoeba_server操作:
mkdir /home/oldboy/tools -p mkdir /application/amoeba/ -p cd /home/oldboy/tools/ 上传两个文件: 1.jdk-6u37-linux-i586-rpm 2.amoeba-mysql-3.0.5-RC-distribution
配置Java环境变量:
vim /etc/profile 添加如下
vim /etc/profile 添加如下####for java export JAVA_HOME=/usr/java/jdk1.6.0_37 export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar export PATH=$PATH:$JAVA_HOME/bin #####for amoeba export AMOEBA_HOME=/application/amoeba/ export PATH=$PATH:$AMOEBA_HOME/bin
使配置文件生效 . /etc/profile 查看Java版本号 java -version
操作:
1.
chmod 755 jdk-6u37-linux-i586-rpm.bin ./jdk-6u37-linux-i586-rpm.bin
2.
解压: unzip amoeba-mysql-3.0.5-RC-distribution.zip cd amoeba-mysql-3.0.5-RC cp -rf * /application/amoeba/ cd /application/amoeba/ 授权: find amoeba/* -type f|xargs chmod 644 find bin/* -type f|xargs chmod 755 注意:最好用ls -la查看一下权限是否修改成功
3.修改配置文件
a.dbServers.xml
<?xml version="1.0" encoding="gbk"?>
<!DOCTYPE amoeba:dbServers SYSTEM "dbserver.dtd">
<amoeba:dbServers xmlns:amoeba="http://amoeba.meidusa.com/">
<!--
Each dbServer needs to be configured into a Pool,
每个dbServer需要配置一个pool,如果多台平等的mysql需要进行loadBalance,
平台已经提供一个具有负载均衡能力的objectPool:
简单的配置是属性加上virtual="true",该Pool不允许配置factoryConfig
或者自己写一个ObjectPool
such as 'multiPool' dbServer
-->
<dbServer name="abstractServer" abstractive="true">
<factoryConfig class="com.meidusa.amoeba.mysql.net.MysqlServerConnectionFactory">
<property name="manager">${defaultManager}</property>
<property name="sendBufferSize">64</property>
<property name="receiveBufferSize">128</property>
<!-- mysql port -->
<property name="port">3306</property>
\\这个是后端数据的端口
<!-- mysql schema -->
<property name="schema">test</property>
\\这个是后端默认的数据库
<!-- mysql user -->
<property name="user">zy</property>
<!-- mysql password
<property name="password">123456</property>
-->
</factoryConfig>
<poolConfig class="com.meidusa.amoeba.net.poolable.PoolableObjectPool">
<property name="maxActive">500</property>
<property name="maxIdle">500</property>
<property name="minIdle">10</property>
<property name="minEvictableIdleTimeMillis">600000</property>
<property name="timeBetweenEvictionRunsMillis">600000</property>
<property name="testOnBorrow">true</property>
<property name="testWhileIdle">true</property>
</poolConfig>
</dbServer>
\\下面的配置是定义一个主节点和一个从节点。
<dbServer name="master" parent="abstractServer"> \\定义一个主节点
<factoryConfig>
<!-- mysql ip -->
<property name="ipAddress">172.16.1.52</property>
<property name="user">root</property> \\连接数据库的用户名
<property name="password">123456</property> \\连接数据库的密码
</factoryConfig>
</dbServer>
<dbServer name="slave" parent="abstractServer"> \\定义一个从节点
<factoryConfig>
<!-- mysql ip -->
<property name="ipAddress">172.16.1.51</property>
<property name="user">root</property>
<property name="password">123456</property>
</factoryConfig>
</dbServer>
\\定义池,把master和slave加入
<dbServer name="pool" virtual="true"> \\server1是要把master节点加入
<poolConfig class="com.meidusa.amoeba.server.MultipleServerPool">
<!-- Load balancing strategy: 1=ROUNDROBIN , 2=WEIGHTBASED , 3=HA--> < ! -- 负载均衡参数1=ROUNDROBIN , 2=WEIGHTBASED , 3=HA-->
<property name="loadbalance">1</property>
<!-- Separated by commas,such as: server1,server2,server1 -->
<property name="poolNames">master</property> <!--
b.amoeba.xml
<?xml version="1.0" encoding="gbk"?>
<!DOCTYPE amoeba:configuration SYSTEM "amoeba.dtd">
<amoeba:configuration xmlns:amoeba="http://amoeba.meidusa.com/">
<proxy>
<!-- service class must implements com.meidusa.amoeba.service.Service -->
<service name="Amoeba for Mysql" class="com.meidusa.amoeba.net.ServerableConnectionManager">
<!-- port -->
<property name="port">3306</property>
<!-- bind ipAddress --> <!--
<property name="ipAddress">127.0.0.1</property>
\\ --> <property name="manager">${clientConnectioneManager}</property>
<property name="connectionFactory">
<bean class="com.meidusa.amoeba.mysql.net.MysqlClientConnectionFactory">
<property name="sendBufferSize">128</property>
<property name="receiveBufferSize">64</property>
</bean>
</property>
<property name="authenticator">
<bean class="com.meidusa.amoeba.mysql.server.MysqlClientAuthenticator">
<property name="user">zy</property>
<property name="password">123456</property>
<property name="filter">
<bean class="com.meidusa.amoeba.server.IPAccessController">
<property name="ipFile">${amoeba.home}/conf/access_list.conf</property>
</bean>
</property>
</bean>
</property>
</service>
<!-- server class must implements com.meidusa.amoeba.service.Service -->
<service name="Amoeba Monitor Server" class="com.meidusa.amoeba.monitor.MonitorServer">
<!-- port -->
<!-- default value: random number
<property name="port">3306</property>
-->
<!-- bind ipAddress -->
<property name="ipAddress">127.0.0.1</property>
<property name="daemon">true</property>
<property name="manager">${clientConnectioneManager}</property>
<property name="connectionFactory">
<bean class="com.meidusa.amoeba.monitor.net.MonitorClientConnectionFactory"></bean>
</property>
</service>
<runtime class="com.meidusa.amoeba.mysql.context.MysqlRuntimeContext">
<!-- proxy server net IO Read thread size -->
<property name="readThreadPoolSize">20</property> <!-- proxy server client process thread size -->
<property name="clientSideThreadPoolSize">30</property> <!-- mysql server data packet process thread size --> <property name="serverSideThreadPoolSize">30</property> <!-- per connection cache prepared statement size -->
<property name="statementCacheSize">500</property> <!-- query timeout( default: 60 second , TimeUnit:second) -->
<property name="queryTimeout">60</property>
</runtime> </proxy>
<!-- Each ConnectionManager will start as thread manager responsible for the Connection IO read , Death Detection <connectionManagerList>
<connectionManager name="clientConnectioneManager" class="com.meidusa.amoeba.net.MultiConnectionManagerWrapper">
<property name="subManagerClassName">com.meidusa.amoeba.net.ConnectionManager</property> <!--
default value is avaliable Processors
<property name="processors">5</property> -->
</connectionManager>
<connectionManager name="defaultManager" class="com.meidusa.amoeba.net.MultiConnectionManagerWrapper">
<property name="subManagerClassName">com.meidusa.amoeba.net.AuthingableConnectionManager</property>
<!-- default value is avaliable Processors
<property name="processors">5</property>
-->
</connectionManager>
</connectionManagerList> <!-- default using file loader -->
<dbServerLoader class="com.meidusa.amoeba.context.DBServerConfigFileLoader">
<property name="configFile">${amoeba.home}/conf/dbServers.xml</property> <
/dbServerLoader>
<queryRouter class="com.meidusa.amoeba.mysql.parser.MysqlQueryRouter">
<property name="ruleLoader">
<bean class="com.meidusa.amoeba.route.TableRuleFileLoader">
<property name="ruleFile">${amoeba.home}/conf/rule.xml</property> <property name="functionFile">${amoeba.home}/conf/ruleFunctionMap.xml</property> </bean> </property>
<property name="sqlFunctionFile">${amoeba.home}/conf/functionMap.xml</property>
<property name="LRUMapSize">1500</property>
<property name="defaultPool">pool</property>
<property name="writePool">master</property>
<property name="readPool">salve</property>
<property name="needParse">true</property>
</queryRouter> </amoeba:configuration>
到此配置文件修改完成
启动服务:
/application/amoeba/bin/launcher start#启动 ss -lntup|grep java #查看启动状态 注意防火墙/etc/iptables status
最后远程登录测试
mysql -uzy -p123456 -h10.0.0.5 -P3306 mysql> use zy mysql> create table tset(id ,int(4),name ,varchar(12)); mysql> insert into test values(1,'oldboy'); mysql> select * from test;
主从同步完成后关掉从库线程
主从同步完成后关掉从库线程 mysql> stop slave sql_thread; 再插入一条数据 mysql> insert into test values(2,'oldboy'); mysql> select * from test; 此时在这里是看不到插入的数据,这时候数据已经到主库里面, 到此实现了主库的写,然后将从库线程打开就能够查到数据, 证明实现了从库的读!