目录
一、MMM 简介
1. 概述
MMM(Master-Master replication manager for MySQL)是一套支持双主故障切换和双主日常管理的脚本程序。MMM 使用 Perl 语言开发,主要用来监控和管理 MySQL Master-Master(双主)复制,可以说是 mysql 主主复制管理器。虽然叫做双主复制,但是业务上同一时刻只允许对一个主进行写入,另一台备选主上提供部分读服务,以加速在主主切换时刻备选主的预热,可以说 MMM 这套脚本程序一方面实现了故障切换的功能,另一方面其内部附加的工具脚本也可以实现多个 slave 的 read 负载均衡。MMM 是关于 MySQL 主主复制配置的监控、故障转移和管理的一套可伸缩的脚本套件(在任何时候只有一个节点可以被写入)。这个套件也能对居于标准的主从配置的任意数量的从服务器进行读负载均衡,所以可以用它在一组居于复制的服务器启动虚拟 IP,除此之外,它还有实现数据备份、节点之间重新同步功能的脚本。
MMM 提供了自动和手动两种方式移除一组服务器中复制延迟较高的服务器的虚拟 IP,同时它还可以备份数据,实现两节点之间的数据同步等。由于 MMM 无法完全的保证数据一致性,所以 MMM 适用于对数据的一致性要求不是很高,但是又想最大程度的保证业务可用性的场景。MySQL本身没有提供 replication failover 的解决方案,通过 MMM 方案能实现服务器的故障转移,从而实现 MySQL 的高可用。对于那些对数据的一致性要求很高的业务,不建议采用 MMM 这种高可用架构。
2. 优缺点
- 优点:高可用性,扩展性好,出现故障自动切换,对于主主同步,在同一时间只提供一台数据库写操作,保证的数据的一致性。
- 缺点:Monitor 节点是单点,可以结合 Keepalived 实现高可用。
3. 工作原理
MMM 是一套灵活的脚本程序,基于 perl 实现,用来对 mysql replication 进行监控和故障迁移,并能管理 mysql Master-Master 复制的配置(同一时间只有一个节点是可写的)。MMM 的主要功能通过以下三个脚本提供:
- mmm_mond:监视守护进程,它执行所有监视工作并做出有关角色切换的所有决定等等。此脚本需要在监管机上运行。
- mmm_agentd:运行在每个 mysql 服务器上(Master 和 Slave)的代理进程,完成监控的探针工作和执行简单的远端服务设置。此脚本需要在被监管机上运行。
- mmm_control:一个简单的脚本,提供管理 mmm_mond 进程的命令。
mysql-mmm 的监管端会提供多个虚拟 IP(VIP),包括一个可写 VIP,多个可读 VIP。通过监管的管理,这些 IP 会绑定在可用 MySQL 之上,当某一台 MySQL 宕机时,监管会将 VIP 迁移至其它 MySQL。在整个监管过程中,需要在 MySQL 中添加相关授权用户,以便让 MySQL 可以支持监理机的维护。授权的用户包括一个 mmm_monitor 用户和一个 mmm_agent 用户,如果想使用 mmm 的备份工具则还要添加一个 mmm_tools 用户。
4. 典型用例
(1)双节点设置
双节点架构如图1 所示。

在双节点主-主设置中,MMM 使用五个 IP:每个节点的单个永久 IP,两个读取 VIP(只读)和 1 个写入 VIP(更新)。最后三个 IP 在节点之间迁移,具体取决于节点可用性。通常在没有复制延迟时,活动主库有 2 个 VIP(读写),备用主库有 1 个读VIP(只读)。如果发生故障,读写操作都会迁移到工作节点。
(2)双主 + 一个/多个从
这种架构如图2 所示。

5. 系统需求
对于使用 n 个 MySQL 服务器的 MMM 设置,有以下需求:
- n + 1 个主机:每个 MySQL 服务器一个主机; MMM 监视器的一个主机。
- 2 * n + 1 个 IP 地址:每个主机一个固定 IP + 读角色一个 VIP,一个写入 VIP。
- monitor user:具有用于 MMM 监视器的 REPLICATION CLIENT 特权的 MySQL 用户。
- agent user:具有 SUPER,REPLICATION CLIENT,MMM 代理进程权限的 MySQL 用户。
- replication user:具有 REPLICATION SLAVE 权限的 MySQL 用户,用于复制。
- tools user:具有 SUPER,REPLICATION CLIENT,RELOAD for MMM 工具权限的 MySQL 用户。
监控主机需要安装以下支持包:
- perl
- fping(如果想以非 root 用户身份运行 mmm_mond)
- Perl 模块:
- Algorithm::Diff
- Class:Singleton
- DBI and DBD::mysql
- File::Basename
- File::stat
- File::Temp
- Log::Dispatch
- Log::Log4perl
- Mail::Send
- Net::Ping
- Proc::Daemon
- Thread::Queue
- Time::HiRes
对于节点主机,初始应该在所有 MySQL 服务器的配置中设置 read_only=1,MMM 将使用 active_master_role 在活动主机上将其更改为 read_only=0。主机需要安装以下支持包:
- perl
- iproute
- send_arp (solaris)
- Perl模块:
- Algorithm::Diff
- DBI and DBD::mysql
- File::Basename
- File::stat
- Log::Dispatch
- Log::Log4perl
- Mail::Send
- Net::ARP (linux)
- Proc::Daemon
- Time::HiRes
如果要使用 MMM 工具(mmm_backup,mmm_restore,mmm_clone),则必须将 LVM 用于 MySQL 数据库和日志所在的分区。注意,需要对回滚段空间进行自由物理扩展,参见“Estimating Undo Space needed for LVM Snapshot”。MMM 工具还需要以下 perl 模块:
- Path::Class
- Data::Dumper
二、实验设计
1. 基本环境
- 操作系统版本:CentOS Linux release 7.2.1511 (Core)
- MySQL版本:5.6.14
- 主机信息:见表1
角色 | IP | 主机名 | 网卡 | server_id |
DB1 | 172.16.1.125 | hdp2 | ens32 | 125 |
DB2 | 172.16.1.126 | hdp3 | ens32 | 126 |
Monitor | 172.16.1.127 | hdp4 | - | - |
表1
- VIP信息:见表2
VIP | 角色 | 描述 |
172.16.1.100 | write | 应用程序连接该VIP对主库进行写请求 |
172.16.1.210 | read | 应用程序连接该VIP进行读请求 |
172.16.1.211 | read | 应用程序连接该VIP进行读请求 |
表2
2. 架构设计
实验架构如图3 所示。

三、MMM 安装配置
1. 配置双主复制
双主复制的详细配置步骤可以参考这篇文章:“MySQL主从复制与主主复制 - 那一叶随风 - 博客园”,这里从略。
2. 安装 MMM
在三台主机执行下面的 yum 命令安装 MMM 软件包。
yum -y install mysql-mmm-*
3. 建立数据库用户
在 DB1、DB2 中建立 mmm_agent 和 mmm_monitor 用户。
grant super,replication client,process on *.* to 'mmm_agent'@'%' identified by '123456';
grant replication client on *.* to 'mmm_monitor'@'%' identified by '123456';
4. 配置 MMM
(1)通用配置
编辑 DB1 上的 /etc/mysql-mmm/mmm_common.conf 文件,内容如下:
active_master_role writer
<host default>
cluster_interface ens32
pid_path /var/run/mmm_agentd.pid
bin_path /usr/libexec/mysql-mmm/
replication_user repl
replication_password 123456
agent_user mmm_agent
agent_password 123456
</host>
<host db1>
ip 172.16.1.125
mode master
peer db2
</host>
<host db2>
ip 172.16.1.126
mode master
peer db1
</host>
<role writer>
hosts db1, db2
ips 172.16.1.100
mode exclusive
</role>
<role reader>
hosts db1, db2
ips 172.16.1.210, 172.16.1.211
mode balanced
</role>
主要配置项说明:
- active_master_role:活动主机角色名称,agent 与 monitor 使用。
- replication_user:用于复制的用户。
- agent_user:mmm-agent 用户。
- host段中的mode:标明是否为主或者备选主,或者从库。
- role段中的mode:exclusive 为独占模式,同一时刻只能有一个主。balanced 可能有多个 ips,ips 将在主机之间平衡。
- <role write> 中的 hosts:表示目前的主库和备选主的真实主机 IP 或者主机名,ips 为对外提供的虚拟机 ip 地址。
- <role readr> 中的 hosts:代表从库真实的 ip 和主机名,ips 代表从库的虚拟 ip 地址。
将该文件复制到其它所有节点(DB2、Monitor)。
scp /etc/mysql-mmm/mmm_common.conf 172.16.1.126:/etc/mysql-mmm/
scp /etc/mysql-mmm/mmm_common.conf 172.16.1.127:/etc/mysql-mmm/
(2)agent 配置
DB1 的 /etc/mysql-mmm/mmm_agent.conf 文件内容为:
include mmm_common.conf
this db1
DB2 的 /etc/mysql-mmm/mmm_agent.conf 文件内容为:
include mmm_common.conf
this db2
(3)monitor 配置
Monitor 上的 /etc/mysql-mmm/mmm_mon.conf 文件内容为:
include mmm_common.conf
<monitor>
ip 172.16.1.127
pid_path /var/run/mmm_mond.pid
bin_path /usr/libexec/mysql-mmm
status_path /var/lib/mysql-mmm/mmm_mond.status
ping_ips 172.16.1.125,172.16.1.126
auto_set_online 60
</monitor>
<host default>
monitor_user mmm_monitor
monitor_password 123456
</host>
debug 0
auto_set_online 表示将节点状态从 AWAITING_RECOVERY 切换到 ONLINE 之前等待的秒数,0 表示已禁用。
四、功能测试
1. 启动 MMM
(1)启动 agent
在 DB1、DB2 上执行:
/etc/init.d/mysql-mmm-agent start
/etc/init.d/mysql-mmm-agent start
/etc/init.d/mysql-mmm-agent 文件内容分如下:
#!/bin/sh
#
# mysql-mmm-agent This shell script takes care of starting and stopping
# the mmm agent daemon.
#
# chkconfig: - 64 36
# description: MMM Agent.
# processname: mmm_agentd
# config: /etc/mmm_agent.conf
# pidfile: /var/run/mmm_agentd.pid
# Cluster name (it can be empty for default cases)
CLUSTER=''
#-----------------------------------------------------------------------
# Paths
if [ "$CLUSTER" != "" ]; then
MMM_AGENTD_BIN="/usr/sbin/mmm_agentd @$CLUSTER"
MMM_AGENTD_PIDFILE="/var/run/mmm_agentd-$CLUSTER.pid"
else
MMM_AGENTD_BIN="/usr/sbin/mmm_agentd"
MMM_AGENTD_PIDFILE="/var/run/mmm_agentd.pid"
fi
echo "Daemon bin: '$MMM_AGENTD_BIN'"
echo "Daemon pid: '$MMM_AGENTD_PIDFILE'"
#-----------------------------------------------------------------------
# See how we were called.
case "$1" in
start)
# Start daemon.
echo -n "Starting MMM Agent daemon... "
if [ -s $MMM_AGENTD_PIDFILE ] && kill -0 `cat $MMM_AGENTD_PIDFILE` 2> /dev/null; then
echo " already running."
exit 0
fi
$MMM_AGENTD_BIN
if [ "$?" -ne 0 ]; then
echo "failed"
exit 1
fi
echo "Ok"
exit 0
;;
stop)
# Stop daemon.
echo -n "Shutting down MMM Agent daemon"
if [ -s $MMM_AGENTD_PIDFILE ]; then
pid="$(cat $MMM_AGENTD_PIDFILE)"
cnt=0
kill "$pid"
while kill -0 "$pid" 2>/dev/null; do
cnt=`expr "$cnt" + 1`
if [ "$cnt" -gt 15 ]; then
kill -9 "$pid"
break
fi
sleep 2
echo -n "."
done
echo " Ok"
exit 0
fi
echo " not running."
exit 0
;;
status)
echo -n "Checking MMM Agent process:"
if [ ! -s $MMM_AGENTD_PIDFILE ]; then
echo " not running."
exit 3
fi
pid="$(cat $MMM_AGENTD_PIDFILE)"
if ! kill -0 "$pid" 2> /dev/null; then
echo " not running."
exit 1
fi
echo " running."
exit 0
;;
restart|reload)
$0 stop
$0 start
exit $?
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
;;
esac
exit 1
(2)启动监控
在 Monitor 上执行:
/etc/init.d/mysql-mmm-monitor start
/etc/init.d/mysql-mmm-monitor 文件内容分如下:
#!/bin/sh
#
# mysql-mmm-monitor This shell script takes care of starting and stopping
# the mmm monitoring daemon.
#
# chkconfig: - 64 36
# description: MMM Monitor.
# processname: mmm_mond
# config: /etc/mmm_mon.conf
# pidfile: /var/run/mmm_mond.pid
# Cluster name (it can be empty for default cases)
CLUSTER=''
#-----------------------------------------------------------------------
# Paths
if [ "$CLUSTER" != "" ]; then
MMM_MOND_BIN="/usr/sbin/mmm_mond @$CLUSTER"
MMM_MOND_PIDFILE="/var/run/mmm_mond-$CLUSTER.pid"
else
MMM_MOND_BIN="/usr/sbin/mmm_mond"
MMM_MOND_PIDFILE="/var/run/mmm_mond.pid"
fi
echo "Daemon bin: '$MMM_MOND_BIN'"
echo "Daemon pid: '$MMM_MOND_PIDFILE'"
#-----------------------------------------------------------------------
# See how we were called.
case "$1" in
start)
# Start daemon.
echo -n "Starting MMM Monitor daemon: "
if [ -s $MMM_MOND_PIDFILE ] && kill -0 `cat $MMM_MOND_PIDFILE` 2> /dev/null; then
echo " already running."
exit 0
fi
$MMM_MOND_BIN
if [ "$?" -ne 0 ]; then
echo "failed"
exit 1
fi
echo "Ok"
exit 0
;;
stop)
# Stop daemon.
echo -n "Shutting down MMM Monitor daemon: "
if [ -s $MMM_MOND_PIDFILE ]; then
pid="$(cat $MMM_MOND_PIDFILE)"
cnt=0
kill "$pid"
while kill -0 "$pid" 2>/dev/null; do
cnt=`expr "$cnt" + 1`
if [ "$cnt" -gt 15 ]; then
kill -9 "$pid"
break
fi
sleep 2
echo -n "."
done
echo " Ok"
exit 0
fi
echo " not running."
exit 0
;;
status)
echo -n "Checking MMM Monitor process:"
if [ ! -s $MMM_MOND_PIDFILE ]; then
echo " not running."
exit 3
fi
pid="$(cat $MMM_MOND_PIDFILE)"
if ! kill -0 "$pid" 2> /dev/null; then
echo " not running."
exit 1
fi
echo " running."
exit 0
;;
restart|reload)
$0 stop
$0 start
exit $?
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
;;
esac
exit 1
(3)检查 MMM 启动后的状态
mmm 启动成功后,在 Monitor 上执行 mmm_control show 和 mmm_control checks 命令结果如下:
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/ONLINE. Roles: reader(172.16.1.210)
db2(172.16.1.126) master/ONLINE. Roles: reader(172.16.1.211), writer(172.16.1.100)
[root@hdp4~]#
[root@hdp4~]#mmm_control checks
db2 ping [last change: 2018/08/02 08:57:38] OK
db2 mysql [last change: 2018/08/02 08:57:38] OK
db2 rep_threads [last change: 2018/08/02 08:57:38] OK
db2 rep_backlog [last change: 2018/08/02 08:57:38] OK: Backlog is null
db1 ping [last change: 2018/08/02 08:57:38] OK
db1 mysql [last change: 2018/08/02 08:57:38] OK
db1 rep_threads [last change: 2018/08/02 08:57:38] OK
db1 rep_backlog [last change: 2018/08/02 08:57:38] OK: Backlog is null
2. 测试切换
(1)停止 DB1 上的 MySQL 服务
service mysql stop
查看状态,DB1 上的 VIP reader(172.16.1.210)自动迁移到 DB2 上。
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/HARD_OFFLINE. Roles:
db2(172.16.1.126) master/ONLINE. Roles: reader(172.16.1.210), reader(172.16.1.211), writer(172.16.1.100)
[root@hdp4~]#
(2)启动 DB1 上的 MySQL 服务
service mysql start
一分钟之后,状态恢复:
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/ONLINE. Roles: reader(172.16.1.210)
db2(172.16.1.126) master/ONLINE. Roles: reader(172.16.1.211), writer(172.16.1.100)
[root@hdp4~]#
(3)停止 DB2 上的 MySQL 服务
service mysql stop
DB2 上负责读的 VIP(172.16.1.211)以及负责写的 VIP(172.16.1.100)会自动迁移到 DB1 上。
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/ONLINE. Roles: reader(172.16.1.210), reader(172.16.1.211), writer(172.16.1.100)
db2(172.16.1.126) master/HARD_OFFLINE. Roles:
[root@hdp4~]#
(4)启动 DB2 上的 MySQL 服务
service mysql start
一分钟之后,DB1 上负责读的 VIP(172.16.1.210)自动迁移到 DB2 上,但是负责写的 VIP(172.16.1.100),仍在 DB1 上。
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/ONLINE. Roles: reader(172.16.1.211), writer(172.16.1.100)
db2(172.16.1.126) master/ONLINE. Roles: reader(172.16.1.210)
[root@hdp4~]#
(5)只读节点上 stop slave
在 DB2 上停止复制:
mysql> stop slave;
查看状态,DB2 上的 VIP(172.16.1.210)会自动迁移到 DB1 上。
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/ONLINE. Roles: reader(172.16.1.210), reader(172.16.1.211), writer(172.16.1.100)
db2(172.16.1.126) master/REPLICATION_FAIL. Roles:
[root@hdp4~]#
(6)只读节点上 start slave
在 DB2 上启动复制:
mysql> start slave;
状态恢复:
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/ONLINE. Roles: reader(172.16.1.211), writer(172.16.1.100)
db2(172.16.1.126) master/ONLINE. Roles: reader(172.16.1.210)
[root@hdp4~]#
(7)读写节点上 stop slave
在 DB1 上停止复制:
mysql> stop slave;
查看状态无任何变化。理论上也应该是对现有的环境无任何影响。
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/ONLINE. Roles: reader(172.16.1.211), writer(172.16.1.100)
db2(172.16.1.126) master/ONLINE. Roles: reader(172.16.1.210)
[root@hdp4~]#
(8)停止 MMM 监控主机上的 monitor 服务
/etc/init.d/mysql-mmm-monitor stop
VIP 都还在之前的节点上:
[root@hdp2~]#ip a | grep ens32
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
inet 172.16.1.125/24 brd 172.16.1.255 scope global ens32
inet 172.16.1.100/32 scope global ens32
inet 172.16.1.211/32 scope global ens32
[mysql@hdp3~]$ip a | grep ens32
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
inet 172.16.1.126/24 brd 172.16.1.255 scope global ens32
inet 172.16.1.210/32 scope global ens32
(9)启动 MMM 监控服务
/etc/init.d/mysql-mmm-monitor start
对 DB1 和 DB2 上的相关服务无影响。
[root@hdp4~]#mmm_control show
db1(172.16.1.125) master/ONLINE. Roles: reader(172.16.1.211), writer(172.16.1.100)
db2(172.16.1.126) master/ONLINE. Roles: reader(172.16.1.210)
[root@hdp4~]#
(10)查看监控日志
以上的角色切换的过程都在监控日志中记录:
[root@hdp4~]#tail -f /var/log/mysql-mmm/mmm_mond.log
...
2018/08/02 09:07:46 FATAL State of host 'db1' changed from ONLINE to HARD_OFFLINE (ping: OK, mysql: not OK)
2018/08/02 09:10:53 FATAL State of host 'db1' changed from HARD_OFFLINE to AWAITING_RECOVERY
2018/08/02 09:11:54 FATAL State of host 'db1' changed from AWAITING_RECOVERY to ONLINE because of auto_set_online(60 seconds). It was in state AWAITING_RECOVERY for 61 seconds
2018/08/02 09:14:06 FATAL State of host 'db2' changed from ONLINE to HARD_OFFLINE (ping: OK, mysql: not OK)
2018/08/02 09:16:22 FATAL State of host 'db2' changed from HARD_OFFLINE to AWAITING_RECOVERY
2018/08/02 09:17:24 FATAL State of host 'db2' changed from AWAITING_RECOVERY to ONLINE because of auto_set_online(60 seconds). It was in state AWAITING_RECOVERY for 62 seconds
2018/08/02 09:20:02 FATAL State of host 'db2' changed from ONLINE to REPLICATION_FAIL
2018/08/02 09:22:14 FATAL State of host 'db2' changed from REPLICATION_FAIL to ONLINE