架构组件简介

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读写分离及MMM高可用架构测试_第1张图片

此架构以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      writer

    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


    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.conf

        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_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 的中设置prefer=db1,这样在db1恢复正常之后,就可以再次被切换为写库了。