MySQL-Proxy是處在你的MySQL數據庫客戶和服務端之間的程序,它還支持嵌入性腳本語言Lua。這個代理可以用來分析、監控和變換(transform)通信數據,它支持非常廣泛的使用場景:
負載平衡和故障轉移處理
查詢分析和日誌
SQL宏(SQL macros)
查詢重寫(query rewriting)
執行shell命令
MySQL Proxy更強大的一項功能是實現“讀寫分離(Read/Write Splitting)”。基本的原理是讓主數據庫處理事務性查詢,而從數據庫處理SELECT查詢。數據庫複製被用來把事務性查詢導致的變更同步到集羣中的從數據庫。
MMM簡介
MMM即Master-Master Replication Manager for MySQL(mysql主主複製管理器),是關於mysql主主複製配置的監控、故障轉移和管理的一套可伸縮的腳本套件(在任何時候只有一個節點可以被寫入),這個套件也能基於標準的主從配置的任意數量的從服務器進行讀負載均衡,所以你可以用它來在一組居於複製的服務器啓動虛擬ip,除此之外,它還有實現數據備份、節點之間重新同步功能的腳本。
MySQL本身沒有提供replication failover的解決方案,通過MMM方案能實現服務器的故障轉移,從而實現mysql的高可用。
MMM項目來自 Google:http://code.google.com/p/mysql-master-master
官方網站爲:http://mysql-mmm.org
MMM主要功能由下面三個腳本提供:
mmm_mond :負責所有的監控工作的監控守護進程,決定節點的移除等等
mmm_agentd :運行在mysql服務器上的代理守護進程,通過簡單遠程服務集提供給監控節點
mmm_control:通過命令行管理mmm_mond進程
關於此架構的優缺點:
優點:安全性、穩定性高,可擴展性好,當主服務器掛掉以後,另一個主立即接管,其他的從服務器能自動切換,不用人工干預。
缺點:至少三個節點,對主機的數量有要求,需要實現讀寫分離,可以在程序擴展上比較難實現。同時對主從(雙主)同步延遲要求比較高!因此不適合數據安全非常嚴格的場合。
實用場所:高訪問量,業務增長快,並且要求實現讀寫分離的場景。
==============================================================
主機信息:
功能 | mysql Server-id | IP | hostname |
monitor | 192.168.9.159 | proxy | |
DB1 | Server-id=1 | 192.168.9.157 | DB01 |
DB2 | Server-id=2 | 192.168.9.158 | DB02 |
虛擬ip分配:
Num | vip | Role |
01 | 192.168.9.154 | Write |
02 | 192.168.9.155 | Read |
03 | 192.168.9.156 | Read |
此架構以mysql作爲數據庫,兩臺數據庫DB01、DB02主從複製(Master-Slave)的方式來同步數據,再通過讀寫分離(MySQL-Proxy)+HA(mysql-mmm)來提升後端數據庫的併發負載及ha能力。
前端應用通過訪問mysql-proxy中定義的proxy-address及端口訪問數據庫,此間mysql-proxy會通過其配置文件中定義的proxy-backend-addresses和proxy-read-only-backend-addresses分配到相應的數據庫。如果將proxy-backend-addresses和proxy-read-only-backend-addresses定義成mysql-mmm中定義的vip,就可以與mysql-mmm的故障切換功能相結合。具體實施過程見下文。
Mysql主從複製應該不難,在這裏我就不再贅述。下面是一些需要注意的地方:
1.1服務器參數
[DB01 服務器]
################# master ######################### server-id = 1 binlog-ignore-db=mysql replicate-ignore-db=mysql log-bin=/usr/local/mysql/binlog/master-bin binlog_format=mixed expire_logs_days= 30 ##################### slave########################## relay-log=/usr/local/mysql/binlog/slave-relay-bin
[DB02 服務器]
################# master ######################### server-id = 2 binlog-ignore-db=mysql replicate-ignore-db=mysql log-bin=/usr/local/mysql/binlog/master-bin binlog_format=mixed expire_logs_days= 30 ##################### slave########################## relay-log=/usr/local/mysql/binlog/slave-relay-bin
1.2 操作步驟
以下操作均在root用戶登錄mysql之後操作。
# DB01 DB02 服務器停止同步 STOP SLAVE; # DB01 DB02 服務器清空Master日誌 RESET MASTER; # DB01 DB02 服務器授權同步賬戶 GRANT REPLICATION SLAVE ON *.* TO 'slave'@'%' IDENTIFIED BY 'slave'; FLUSH PRIVILEGES; # DB01 DB02 服務器鎖表(鎖表狀態下不能終止mysql進程,否則會失敗;也可以不鎖表,關閉前臺的所有web服務器) FLUSH TABLES WITH READ LOCK; # 做複製之前最好手動同步一次數據庫。 # 查看 DB01 服務器主機狀態(記錄二進制開始文件,位置) SHOW MASTER STATUS; #DB02服務器鎖表(鎖表狀態下不能終止mysql進程,否則會失敗) FLUSH TABLES WITH READ LOCK; # 修改 DB02 服務器配置 CHANGE MASTER TO MASTER_HOST='192.168.9.157',MASTER_USER='slave', MASTER_PASSWORD='slave',MASTER_LOG_FILE='master-bin.000001',MASTER_LOG_POS=107; # 開啓 DB02 服務器同步進程 START SLAVE; # 查看 DB02 服務器同步狀態是否正常 show slave status\G; # 查看 DB02 服務器主機(記錄二進制開始文件,位置) SHOW MASTER STATUS; # 修改 DB01 服務器配置 CHANGE MASTER TO MASTER_HOST='192.168.9.158',MASTER_USER='slave', MASTER_PASSWORD='slave',MASTER_LOG_FILE='master-bin.000001',MASTER_LOG_POS=107; # 開啓 DB01服務器同步進程 START SLAVE; # 分別查看 DB01、DB02 服務器同步狀態,確定是否成功 show slave status\G; SHOW MASTER STATUS; # 解鎖 DB01、DB02 服務器 UNLOCK TABLES; # 數據測試分別在 DB01、DB02 服務器上創建表插入數據測試
1.3 需要注意的地方
數據庫目錄下的master.info的內容會覆蓋命令行或my.cnf中指定的部分選項,更改配置需刪除master.info
my.cnf中的master配置在MySQL 6.0以後會取消,官方建議使用動態的CHANGE MASTER
如果只指定ignore-db而不指定do-db。則創建數據庫的操作也會同步。
雙主模式使用log-slave-updates參數,會導致數據不一致
log-slave-updates 啓用從屬服務器上的日誌功能,使這臺計算機可以用來構成一個鏡像鏈(A->B->C)。
互爲同步配置實例:
1. DB01 DB02 互爲主從同步test,不同步mysql:
兩個數據庫配置中均設置:binlog-do-db=test,binlog-ignore-db=mysql,replicate-do-db=test,replicate-ignore-db=mysql
2. DB01 DB02 互爲主從只同步test,不同步其他數據庫,新創建的也不會同步
兩個數據庫配置中均設置:binlog-do-db=test,replicate-do-db=test
3. DB01 DB02 互爲主從不同步mysql,同步其他數據庫,譬如創建的新數據庫也會同步
兩個數據庫配置中均設置:binlog-ignore-db=mysql,replicate-ignore-db=mysql
4. DB01 DB02互爲主從同步所有數據庫,包括新建的數據庫
兩個數據庫配置中均不設置上述四項
2.1 場景描述
數據庫Master主服務器DB01:192.168.9.157
數據庫Master主服務器DB02:192.168.9.158
MySQL-Proxy調度服務器:192.168.9.159
以下操作,均是在192.168.10.132即MySQL-Proxy調度服務器上進行的。
2.2 安裝升級系統所需軟件包
yum -y install gcc gcc-c++ autoconf mysql-devel libtool pkgconfig ncurses ncurses-devel wget make glibc glibc-devel gettext automake ntp hdparm dmidecode openssh-clients telnet traceroute pciutils
2.3.1 安裝libevent
wget http://monkey.org/~provos/libevent-2.0.10-stable.tar.gz tar xvfz libevent-2.0.10-stable.tar.gz cd libevent-2.0.10-stable ./configure make&&make install cd ..
2.3.2安裝glib-2
wget http://ftp.gnome.org/pub/gnome/sources/glib/2.22/glib-2.22.5.tar.gz tar xvfz glib-2.22.5.tar.gz cd glib-2.22.5 ./configure make && make install
MySQL-Proxy的讀寫分離主要是通過rw-splitting.lua腳本實現的,因此需要安裝lua。
lua可通過以下方式獲得
從http://www.lua.org/download.html下載源碼包
Lua安裝之前需要先安裝readline6.1,不然會報錯缺少頭文件:
wget ftp://ftp.cwru.edu/pub/bash/readline-6.1.tar.gz tar xvfz readline-6.1.tar.gz cd readline-6.1 ./configure make&&make install cd .. #wget http://www.lua.org/ftp/lua-5.2.0.tar.gz wget http://www.lua.org/ftp/lua-5.1.4.tar.gz tar zxvf lua-5.1.4.tar.gz cd lua-5.1.4 # 64位系統,需在CFLAGS里加上-fPIC vi src/Makefile CFLAGS= -O2 -Wall -fPIC $(MYCFLAGS) make linux make install # pkg-config 環境變量 cp etc/lua.pc /usr/local/lib/pkgconfig/ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig cd ..
2.3.4 安裝mysql-proxy
MySQL-Proxy可通過以下網址獲得:
http://mysql.cdpa.nsysu.edu.tw/Downloads/MySQL-Proxy/
wget http://mysql.cdpa.nsysu.edu.tw/Downloads/MySQL-Proxy/mysql-proxy-0.8.2.tar.gz tar xvfz mysql-proxy-0.8.2.tar.gz cd mysql-proxy-0.8.2 ./configure make && make install #編譯安裝的mysql-proxy貌似沒有讀寫實現分離相關的腳本,所以需要下載安裝 wget http://mysql.cdpa.nsysu.edu.tw/Downloads/MySQL-Proxy/mysql-proxy-0.8.2-linux-glibc2.3-x86-64bit.tar.gz tar xvfz mysql-proxy-0.8.2-linux-glibc2.3-x86-64bit.tar.gz cd mysql-proxy-0.8.2-linux-glibc2.3-x86-64bit/share/doc mv mysql-proxy /usr/local/share/
2.4配置啓動mysql-proxy
2.4.1配置mysql-proxy
vi /etc/mysql-proxy.cnf
#創建配置文件,內容如下
[mysql-proxy] proxy-address=192.168.9.159:3306 proxy-backend-addresses=192.168.9.154:3306 proxy-read-only-backend-addresses=192.168.9.155:3306 proxy-read-only-backend-addresses=192.168.9.156:3306 proxy-lua-script=/usr/local/share/mysql-proxy/rw-splitting.lua keepalive=true log-level=error log-file=/home/logs/mysql-proxy.log
2.4.2 創建mysql-proxy啓動腳本
#創建啓動腳本,內容如下:
vi /etc/init.d/mysql-proxy
#!/bin/sh # # mysql-proxy This script starts and stops the mysql-proxy daemon # # chkconfig: - 78 30 # processname: mysql-proxy # description: mysql-proxy is a proxy daemon to mysql # Source function library. . /etc/rc.d/init.d/functions #LUA_PATH=/usr/local/lib/mysql-proxy/lua/?.lua PROXY_PATH=/usr/local/bin prog="mysql-proxy" # Source networking configuration. . /etc/sysconfig/network # Check that networking is up. [ ${NETWORKING} = "no" ] && exit 0 # Set default mysql-proxy configuration. PROXY_OPTIONS="--daemon --defaults-file=/etc/mysql-proxy.cnf" PROXY_PID=/var/run/mysql-proxy.pid # Source mysql-proxy configuration. if [ -f /etc/sysconfig/mysql-proxy ]; then . /etc/sysconfig/mysql-proxy fi PATH=$PATH:/usr/bin:/usr/local/bin:$PROXY_PATH # By default it's all good RETVAL=0 # See how we were called. case "$1" in start) # Start daemon. echo -n $"Starting $prog: " daemon $NICELEVEL $PROXY_PATH/mysql-proxy $PROXY_OPTIONS --pid-file $PROXY_PID RETVAL=$? echo if [ $RETVAL = 0 ]; then touch /var/lock/subsys/mysql-proxy fi ;; stop) # Stop daemons. echo -n $"Stopping $prog: " killproc $prog RETVAL=$? echo if [ $RETVAL = 0 ]; then rm -f /var/lock/subsys/mysql-proxy rm -f $PROXY_PID fi ;; restart) $0 stop sleep 3 $0 start ;; condrestart) [ -e /var/lock/subsys/mysql-proxy ] && $0 restart ;; status) status mysql-proxy RETVAL=$? ;; *) echo "Usage: $0 {start|stop|restart|status|condrestart}" RETVAL=1 ;; esac exit $RETVAL
2.4.3 啓動mysql-proxy
chmod +x /etc/init.d/mysql-proxy #創建日誌目錄 cd /home mkdir logs chmod 0777 logs # 啓動mysql-proxy /etc/init.d/mysql-proxy start # 停止mysql-proxy /etc/init.d/mysql-proxy stop # 重啓mysql-proxy /etc/init.d/mysql-proxy restart #添加到服務 chkconfig mysql-proxy on #查看mysql-proxy的幫助文件 mysql-proxy –help-all #登錄mysql-proxy mysql -uroot -p -h192.168.9.159 –P3306 #查看已經代理的數據庫 show processlist;
2.5 測試驗證讀寫分離
#1.分別登錄DB01 DB02 stop slave; mysql -uroot -p mysql> stop slave; #2.在DB01的test數據庫建立一個表hh mysql> use test; mysql> create table hh(id int(5),address char(255)); #在DB02的test數據庫建立一個表hh mysql> use test; mysql> create table hh(id int(5),name char(255)); #兩個表名字一樣卻有不同的字段。 #3.在proxy上開啓mysql-proxy並登錄 /etc/init.d/mysql-proxy start mysql -uroot -p -h192.168.9.159 –P3306 #對錶進行查詢,結果顯示DB02上創建的表的結構; mysql> use test; mysql> desc hh; +-------+-----------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-----------+------+-----+---------+-------+ | id | int(5) | YES | | NULL | | | name | char(255) | YES | | NULL | | +-------+-----------+------+-----+---------+-------+ 2 rows in set (0.00 sec) #在當前環境下創建一個表 mysql> create table hr_boy(id int(5),name char(255)); Query OK, 0 rows affected (0.01 sec) #在DB01上對錶進行查詢,結果 mysql> use test; mysql> desc hr_boy; +-------+-----------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-----------+------+-----+---------+-------+ | id | int(5) | YES | | NULL | | | name | char(255) | YES | | NULL | | +-------+-----------+------+-----+---------+-------+ 2 rows in set (0.00 sec)
環境描述:
MMM服務端:192.168.9.159
MMM客戶端:192.168.9.157,192.168.9.158
3.1安裝MMM服務端
編譯安裝需要以下四個組件:
wget http://ftp.osuosl.org/pub/nslu2/sources/Algorithm-Diff-1.1902.tar.gz wget http://ftp.riken.go.jp/pub/pub/lang/CPAN/modules/by-module/Proc/EHOOD/Proc-Daemon-0.03.tar.gz wget http://mysql-master-master.googlecode.com/files/mysql-master-master-1.2.6.tar.gz yum -y install perl-DBD-MySQL
在這裏我們採用yum方式安裝。
3.1.1 先安裝相關的包
yum -y install liblog-log4perl-perl libmailtools-perl liblog-dispatch-perl libclass-singleton-perl libproc-daemon-perl libalgorithm-diff-perl libdbi-perl libdbd-mysql-perl
3.1.2 安裝MMM
wget http://mirrors.ustc.edu.cn/fedora/epel//6/x86_64/epel-release-6-5.noarch.rpm rpm -ivh epel-release-6-5.noarch.rpm yum install -y mysql-mmm-agent mysql-mmm-monitor mysql-mmm-tool
3.2 安裝MMM客戶端(DB01 DB02)
wget http://mirrors.ustc.edu.cn/fedora/epel//6/x86_64/epel-release-6-5.noarch.rpm rpm -ivh epel-release-6-5.noarch.rpm yum install -y mysql-mmm-agent
三臺主機安裝以上軟件後,即可進行配置
3.3 配置MMM客戶端(DB01 DB02)
3.3.1 修改mmm_common.conf
#All generic configuration-options are grouped in a separate file called /etc/mysql-mmm/mmm_common.conf. This file will be the same on all hosts in the system: vi /etc/mysql-mmm/mmm_common.conf #--------------------------------------------------------- active_master_role writer <host default> cluster_interface eth0 pid_path /var/run/mysql-mmm/mmm_agentd.pid bin_path /usr/libexec/mysql-mmm/ replication_user slave replication_password slave agent_user mmm_agent agent_password RepAgent </host> <host db1> ip 192.168.9.157 mode master peer db2 </host> <host db2> ip 192.168.9.158 mode master peer db1 </host> #<host db3> # ip 192.168.100.51 # mode slave #</host> <role writer> hosts db1, db2 ips 192.168.9.154 mode exclusive </role> <role reader> hosts db1, db2 ips 192.168.9.155,192.168.9.156 mode balanced </role> #--------------------------------------------------------- #Don't forget to copy this file to all other hosts (including the monitoring host).
3.3.2 修改mmm_agent.conf
On the database hosts we need toedit /etc/mysql-mmm/mmm_agent.conf. Change “db1” accordingly on the other hosts:
vi /etc/mysql-mmm/mmm_agent.conf #--------------------DB1---------------------------------- include mmm_common.conf this db1 #--------------------------------------------------------- #--------------------DB2---------------------------------- include mmm_common.conf this db2 #---------------------------------------------------------
3.4 配置MMM服務端
3.4.1 修改mmm_mon.conf
On the monitor host we need toedit /etc/mysql-mmm/mmm_mon.conf
vi /etc/mysql-mmm/mmm_mon.conf #--------------------------------------------------------- include mmm_common.conf <monitor> ip 127.0.0.1 pid_path /var/run/mmm_mond.pid bin_path /usr/lib/mysql-mmm/ status_path /var/lib/misc/mmm_mond.status ping_ips 192.168.9.157,192.168.9.158,192.168.9.254 auto_set_online 10 </monitor> <host default> monitor_user mmm_monitor monitor_password RepMonitor </host> debug 0 #---------------------------------------------------------
3.5 設置權限(MMM客戶端) 在所有MMM客戶端爲監控程序創建授權帳號
GRANT ALL PRIVILEGES on *.* to'mmm_agent'@'%' identified by 'RepAgent'; GRANT ALL PRIVILEGES on *.* to'mmm_monitor'@'%' identified by 'RepMonitor'; flush privileges;
3.6 MMM測試
3.6.1 啓動MMM客戶端(DB01 DB02)
將mysql-mmm-agent添加爲服務:
chkconfig mysql-mmm-agent on /etc/init.d/mysql-mmm-agent start Starting MMM Agent Daemon: [ OK ]
以上信息說明客戶端啓動正常
3.6.2 啓動MMM服務器端(monitor)
#(On the monitoring host) Edit/etc/default/mysql-mmm-monitor to enable the monitor:
vi /etc/default/mysql-mmm-monitor #--------------------------------------------------------- ENABLED=1 #--------------------------------------------------------- #Then start it: chkconfig mysql-mmm-monitor on /etc/init.d/mysql-mmm-monitor start
3.6.3 MMM狀態檢查
(1)#Wait some seconds formmmd_mon to start up. After a few seconds you can use mmm_control to check thestatus of the cluster:
[root@Proxy soft]# mmm_control show # Warning: agent on host db2 is not reachable db1(192.168.9.157) master/HARD_OFFLINE. Roles: db2(192.168.9.158) master/HARD_OFFLINE. Roles:
(2)設置hosts online (db1first, because the slaves replicate from this host):
[root@Proxy soft]#mmm_control set_online db1 OK: State of 'db1' changed to ONLINE. Now you can wait some time and check its new roles! [root@Proxy soft]#mmm_control set_online db2 OK: State of 'db2' changed to ONLINE. Now you can wait some time and check its new roles! [root@ Proxy soft]# mmm_control checks all db2 ping [last change: 2013/09/10 21:52:01] OK db2 mysql [last change: 2013/09/10 21:52:04] OK db2 rep_threads [last change: 2013/09/10 21:20:22] OK db2 rep_backlog [last change: 2013/09/10 21:20:22] OK: Backlog is null db1 ping [last change: 2013/09/10 21:20:22] OK db1 mysql [last change: 2013/09/10 21:20:22] OK db1 rep_threads [last change: 2013/09/10 22:16:10] OK db1 rep_backlog [last change: 2013/09/10 21:20:22] OK: Backlog is null
3.6.4模擬宕機測試
隨便找一個客戶端,執行寫操作:
@client[root@mysql-1 ~]# vi /usr/local/mysql/binlog/inserting-into-db.sh #!/bin/bash while true; do mysql -uxxxx -pxxxxxx -h192.168.9.154 –P3306 --database= test -e "insert into test values(null);" sleep 1 ; done; [root@mysql-1 ~]# ./inserting-into-db.sh &
可以看到兩個db中的binlog顯示的server id都是1,也就是說當前情況下db01是作爲寫庫。
#停止db01
[root@DB01 ~]# /etc/init.d/mysqld stop Shutting down MySQL.. SUCCESS!
立即恢復DB1後proxy上查看mmm集羣狀態
檢查mmmDB1的日誌:
[root@DB01 binlog]# tail -f /var/log/mysql-mmm/mmm_agentd.log 2012/02/03 17:46:10 FATAL Couldn't allow writes: ERROR: Can't connect to MySQL (host = 192.168.9.157:3306, user = mmm_agent)! Lost connection to MySQL server at 'reading initial communication packet', system error: 111 2012/02/03 17:46:13 INFO We have some new roles added or old rules deleted! 2012/02/03 17:46:13 INFO Deleted: reader(192.168.9.156), writer(192.168.9.154) 2012/02/03 17:46:13 FATAL Couldn't deny writes: ERROR: Can't connect to MySQL (host = 192.168.9.157:3306, user = mmm_agent)! Lost connection to MySQL server at 'reading initial communication packet', system error: 111 2012/02/03 17:59:48 INFO We have some new roles added or old rules deleted! 2012/02/03 17:59:48 INFO Added: reader(192.168.9.155) 2012/02/03 18:01:12 INFO We have some new roles added or old rules deleted!
從日誌可以看出,db1停止之後,mmm提示connect error,由於當前的寫庫是db1,於是mmm認爲db2上的數據已經不能和db1保持一致了,故把db2的讀角色(reader)遷移到db1上。變成了:
db1(192.168.9.157) master/ONLINE. Roles: reader(192.168.9.157) db2(192.168.9.158) master/ONLINE. Roles: reader(192.168.9.156), writer(192.168.9.154)
但是,若DB1未立即恢復工作,mmm的”mysql”檢查項在10秒後出現報警,認爲db1已經徹底失敗,因此會把db1設置狀態爲hard_offline,把 db2從replication_fail狀態切換到online狀態(因爲db2的mysql至少還活着)同時把上面的所有角色切換到db2上。狀態最 終變爲:
[root@Proxy mysql-mmm]# mmm_control show db1(192.168.9.157) master/HARD_OFFLINE. Roles: db2(192.168.9.158) master/ONLINE. Roles: reader(192.168.9.155), reader(192.168.9.156), writer(192.168.9.154)
很顯然,當DB1或DB2中的其中一臺宕機之後,mmm都會立即將宕機的主機的角色全部轉換到另一臺DB。
仔細分析Mmm的處理步驟大致是:
db1的“mysql”check恢復正常,然後把db1切換到awaiting_recovery狀態。然後mmm判斷db6的宕機時間在正常範圍內,不屬於異常情況,因此自動切換爲online狀態。
把db2中的一個reader角色遷移到db1上。
目前寫庫是db2。
注:可以在exclusive 的<rolewriter>中設置prefer=db1,這樣在db1恢復正常之後,就可以再次被切換爲寫庫了。