關於讀寫分離:
讀寫分離(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; 此時在這裏是看不到插入的數據,這時候數據已經到主庫裏面, 到此實現了主庫的寫,然後將從庫線程打開就能夠查到數據, 證明實現了從庫的讀!