架构组件简介
MySQL-Proxy简介
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进程
关于此架构的优缺点:
优点:安全性、稳定性高,可扩展性好,当主服务器挂掉以后,另一个主立即接管,其他的从服务器能自动切换,不用人工干预。
缺点:至少三个节点,对主机的数量有要求,需要实现读写分离,可以在程序扩展上比较难实现。同时对主从(双主)同步延迟要求比较高!因此不适合数据安全非常严格的场合。
实用场所:高访问量,业务增长快,并且要求实现读写分离的场景。
架构模式(双master零slave)简介
==============================================================
主机信息:
功能 |
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复制实现互为主从双机热备
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互为主从同步所有数据库,包括新建的数据库
两个数据库配置中均不设置上述四项
二、mysql读写分离
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 mysql-proxy安装
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
环境描述:
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 writercluster_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 ip 192.168.9.157 mode master peer db2 ip 192.168.9.158 mode master peer db1 ## ip 192.168.100.51 # mode slave # hosts db1, db2 ips 192.168.9.154 mode exclusive hosts db1, db2 ips 192.168.9.155,192.168.9.156 mode balanced #--------------------------------------------------------- #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.confip 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_user mmm_monitor monitor_password RepMonitor 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 的