一、概述

本文将介绍mysql的MM+Keepalived方案。该方案由两个mysql服务器组成,这两个mysql互为主备。其中一台主作为写服务器,另一台主作为读服务器。通过keepalived软件管理写vip,当承担写服务器的mysql出现故障时,将写vip漂移到读服务器上,实现高可用。

二、节点介绍

本次实验采用2台虚拟机,操作系统版本Centos6.10,mysql版本5.7.25
node1    10.40.16.61  主库  提供写服务
node2    10.40.16.62  主库  提供读服务

还须预留1个vip,现在不用配置,这里先提一下,后面的安装步骤用得到
10.40.16.71  写vip

三、安装

1. 配置双主架构

安利一个自己写的mysql一键安装脚本https://www.cnblogs.com/ddzj01/p/10678296.html
mysql搭建完成后,就可以配置互为主备的架构了。

这样node1和node2就互为主备了

在node2上将数据库设置为只读模式
(root@localhost)[(none)]> set global read_only = 1;

2. 安装keepalive软件

node1&node2:
yum install -y keepalived

四、修改配置文件

1. node1

编辑配置文件/etc/keepalived/keepalived.conf

! Configuration File for keepalived

vrrp_script chk_mysql {
script "/etc/keepalived/check_mysql.sh" # 自定义检查脚本
interval 30 # 设置检查间隔时长,可自行设定
} vrrp_instance VI_1 {
state BACKUP # BACKUP状态,具体意思后面介绍
interface eth0
virtual_router_id 51
priority 100
advert_int 1
nopreempt # 防止主库切换到从库后,主库恢复后自动切换回主库
authentication {
auth_type PASS
auth_pass 1111
}
track_script {
chk_mysql
}
virtual_ipaddress {
10.40.16.71/24 # vip
}
}

编辑检查mysql主库的脚本文件/etc/keepalived/check_mysql.sh

#!/bin/bash
source /root/.bash_profile
###填数据库相关信息###
DB_USER='root'
DB_PASSWD='root'
U_EMAIL='xxxx@163.com'
###################### ###判断如果上次检查的脚本还没执行完,则退出此次执行
if [ `ps -ef | grep -w "$0" | grep -v "grep" | wc -l` -gt 2 ]; then
exit 0
fi
mysql_con="mysql -u$DB_USER -p$DB_PASSWD"
error_log="/etc/keepalived/logs/check_mysql.err" ###如果error_log目录不存在则创建目录
if [ -d /etc/keepalived/logs ]; then
usleep
else
mkdir -p /etc/keepalived/logs
fi ###定义一个简单判断mysql是否可用的函数
function execute_query {
$mysql_con -e "select 1;" 2>> $error_log
} ###定义无法执行查询,且mysql服务异常时的处理函数
function service_error {
echo -e "`date "+%F %H:%M:%S"` ----mysql service error, now stop keepalived----" >> $error_log
service keepalived stop >> $error_log 2>&1
echo "master1 keepalived stopped" | mail -s "master1 keepalived stopped, please take notice!" $U_EMAIL 2>> $error_log
echo -e "\n---------------------------------------------------------\n" >> $error_log
} ###定义无法执行查询,但mysql服务正常的处理函数
function query_error {
echo -e "`date "+%F %H:%M:%S"` ----query error, but mysql service ok, retry after 30s----" >> $error_log
sleep 30
execute_query
if [ $? -ne 0 ]; then
echo -e "`date "+%F %H:%M:%S"` ----still can't execute query----" >> $error_log ###关闭本机mysql
echo -e "`date "+%F %H:%M:%S"` ----stop mysql service----" >> $error_log
service mysql stop &>> $error_log
sleep 2 ###给执行和缓冲时间 ###关闭本机keepalived
echo -e "`date "+%F %H:%M:%S"` ----stop keepalived----" >> $error_log
service keepalived stop &>> $error_log
echo "master1 keepalived stopped" | mail -s "master1 keepalived stopped, please take notice!" $U_EMAIL 2>> $error_log
echo -e "\n---------------------------------------------------------\n" >> $error_log
else
echo -e "`date "+%F %H:%M:%S"` ----query ok after 30s----" >> $error_log
echo -e "\n---------------------------------------------------------\n" >> $error_log
fi
} ###检查开始: 执行查询
execute_query
if [ $? -ne 0 ]; then
service mysql status &> /dev/null
if [ $? -ne 0 ]; then
service_error
else
query_error
fi
fi

chmod +x /etc/keepalived/check_mysql.sh
该脚本的作用有两个
a. mysql进程挂掉了,直接关闭keepalived服务
b. mysql进程正常,但是无法执行查询,比如进程卡死了等等原因。则同时关闭mysql和keepalived

2. node2

编辑配置文件/etc/keepalived/keepalived.conf

! Configuration File for keepalived

vrrp_instance VI_1 {
state BACKUP # BACKUP状态,具体意思后面介绍
interface eth0
virtual_router_id 51
priority 90 # 优先级设置为90,这个值设置比节点1低
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
notify_master /etc/keepalived/notify_master_mysql.sh
virtual_ipaddress {
10.40.16.71/24 # vip
}
}

编辑检查mysql从库的脚本文件/etc/keepalived/notify_master_mysql.sh

#!/bin/bash
source /root/.bash_profile
###当keepalived监测到本机转为MASTER状态时,执行该脚本
###填数据库相关信息###
DB_USER='root'
DB_PASSWD='root'
U_EMAIL='xxxx@163.com'
###################### change_log='/etc/keepalived/logs/state_change.log'
mysql_con="mysql -u$DB_USER -p$DB_PASSWD"
echo -e "`date "+%F %H:%M:%S"` ----master2 keepalived change to MASTER----" >> $change_log ###如果error_log目录不存在则创建目录
if [ -d /etc/keepalived/logs ]; then
usleep
else
mkdir -p /etc/keepalived/logs
fi slave_info() {
###检查从库状态
slave_stat=`$mysql_con -e "show slave status\G"`
Slave_IO_Running=`echo $slave_stat | egrep -w "Slave_IO_Running" | awk '{print $2}'`
Slave_SQL_Running=`echo $slave_stat | egrep -w "Slave_SQL_Running" | awk '{print $2}'`
Master_Log_File=`echo $slave_stat | egrep -w "Master_Log_File" | awk '{print $2}'`
Read_Master_Log_Pos=`echo $slave_stat | egrep -w "Read_Master_Log_Pos" | awk '{print $2}'`
Relay_Master_Log_File=`echo $slave_stat | egrep -w "Relay_Master_Log_File" | awk '{print $2}'`
Exec_Master_Log_Pos=`echo $slave_stat | egrep -w "Exec_Master_Log_Pos" | awk '{print $2}'`
} action() {
###解除read_only属性
echo -e "`date "+%F %H:%M:%S"` ----set read_only = 0 on master2----" >> $change_log
$mysql_con -e "set global read_only = 0;" 2>> $change_log
echo "master2 keepalived change to MASTER,线上数据库切换至master2" | mail -s "master2 keepalived change to MASTER" $U_EMAIL 2>> $change_log
echo -e "---------------------------------------------------------\n" >> $change_log
} slave_info
if [ $Slave_SQL_Running == 'Yes' ]; then
i=0 #一个计数器
###判断从master接收到的binlog是否全部在本地执行(这样仍无法完全确定从库已追上主库,因为无法完全保证io_thread没有延时(但由网络传输问题导致的从库落后的概率很小)
until [ $Master_Log_File == $Relay_Master_Log_File -a $Read_Master_Log_Pos == $Exec_Master_Log_Pos ]
do
if [ $i -lt 10 ]; then #将等待exec_pos追上read_pos的时间限制为20s
echo -e "`date "+%F %H:%M:%S"` ----Relay_Master_Log_File=$Relay_Master_Log_File, Exec_Master_Log_Pos=$Exec_Master_Log_Pos is behind Master_Log_File=$Master_Log_File, Read_Master_Log_Pos=$Read_Master_Log_Pos, wait......" >> $change_log #输出消息到日志,等待exec_pos=read_pos
i=$(($i+1))
sleep 2
slave_info
else
echo -e "The waits time is more than 20s,now force change. Master_Log_File=$Master_Log_File Read_Master_Log_Pos=$Read_Master_Log_Pos Relay_Master_Log_File=$Relay_Master_Log_File Exec_Master_Log_Pos=$Exec_Master_Log_Pos" >> $change_log
action
exit 0
fi
done
action
else
echo -e "master2's slave status is not running,now force change. Master_Log_File=$Master_Log_File Read_Master_Log_Pos=$Read_Master_Log_Pos Relay_Master_Log_File=$Relay_Master_Log_File Exec_Master_Log_Pos=$Exec_Master_Log_Pos" >> $change_log
action
fi

chmod +x /etc/keepalived/notify_master_mysql.sh
该脚本的作用有三个
a. slave sql线程没有运行,直接将从库只读关闭
c. slave sql线程正在运行,如果从库没有延迟,直接将从库只读关闭
b. slave sql线程正在运行,如果从库有延迟,等待一段时间(这个自己设置)再将从库只读关闭

这里解释下keepalived.conf的"state BACKUP"的意思,在Keepalived中有两种模式,分别是master->backup模式和backup->backup模式,这两种模式有什么区别呢?
在master->backup模式下,一旦主库宕掉,虚拟IP会自动漂移到从库,当主库修复后,keepalived启动后,还会把虚拟IP抢过来,即使你设置nopreempt(不抢占)的方式抢占IP的动作也会发生。
在backup->backup模式下,当主库宕掉后虚拟IP会自动漂移到从库上,当原主恢复之后重启keepalived服务,并不会抢占新主的虚拟IP,即使是原主优先级高于从库的优先级别,也不会抢占虚拟IP。
所以,为了减少虚拟IP的漂移次数,生产中我们通常是把修复好的主库当做新主库的备库。因而采用backup->backup模式居多。

五、启动Keepailived

node1&node2:
service keepalived start
注意因为我们使用的是backup->backup模式,所以启动keepalived的顺序需要先启动node1,再启动node2,这样vip才会在node1上。如果先启动node2,再启动node1,node1并不会把虚拟IP抢过来。

六、测试

1. 模拟主库宕机的情况

关闭node1的mysql数据库
service mysql stop

查看node1的keepalived日志/etc/keepalived/logs/check_mysql.err

查看node1和node2的vip,发现vip已经转移到了node2

node2的read_only参数也变成了off      

node2的切换日志可以查看/etc/keepalived/logs/state_change.log
看看有没有收到邮件,呵呵:-)
failover正常!

2. 模拟主库正常,但是无法查询

重新打开node1的mysql数据库和keepalived服务
service mysql start
service keepalived start

重启node2的keepalived服务
service keepalived restart

在node2上将数据库设置为只读模式
(root@localhost)[(none)]> set global read_only = 1;

这样就跟最初的状态一致了,主库是node1,从库是node2,vip在node1上。

在node1上将参数max_connections设置得足够小
(root@localhost)[(none)]> set global max_connections = 2;

在node1上然后多开几个连接,直到出现无法连接的情况。以此来模拟无法查询的情况。

查看node1的keepalived日志/etc/keepalived/logs/check_mysql.err

查看node1,发现keepalived和mysql服务都已经停止
[root@mysqla ~]# service mysql status
MySQL is not running                                    
[root@mysqla ~]# service keepalived status
keepalived is stopped

node2的read_only参数也变成了off

vip也已经漂移过来了
[root@mysqlb ~]# ip a

failover正常!

七、手动切换

还有一个场景就是,如何手工切换。举个例子,node2目前是主库,但是node1通过各种办法修复好了,我想让node1当主库。

重启node1的mysql服务
service mysql restart

这样node2是主库,node1是备库,vip在node2上

在node2上创建手工切换的脚本vi /etc/keepalived/manual_switch_to_master

#!/bin/bash
source /root/.bash_profile
###在master2上手动执行将主库切换回master1的操作
###填数据库相关信息###
DB_USER='root'
DB_PASSWD='root'
U_EMAIL='xxxx@163.com'
MASTER1='10.40.16.61'
MASTER2='10.40.16.62'
REPL_USER='repl'
REPL_PASSWD='123456'
MASTER1_MYSQL_PATH='/usr/local/mysql/bin'
###################### ###如果error_log目录不存在则创建目录
if [ -d /etc/keepalived/logs ]; then
usleep
else
mkdir -p /etc/keepalived/logs
fi mysql_con="mysql -u$DB_USER -p$DB_PASSWD" echo -e "`date "+%F %H:%M:%S"` ----change to BACKUP manually----" >> /etc/keepalived/logs/state_change.log
echo -e "`date "+%F %H:%M:%S"` ----set read_only = 1 on master2----" >> /etc/keepalived/logs/state_change.log
$mysql_con -e "set global read_only = 1;" >> /etc/keepalived/logs/state_change.log ###kill掉当前客户端连接
echo -e "`date "+%F %H:%M:%S"` ----kill current client thread----" >> /etc/keepalived/logs/state_change.log
if [ -e /tmp/kill.sql ]; then
rm -f /tmp/kill.sql &> /dev/null
fi
###这里其实是一个批量kill线程的小技巧
$mysql_con -e 'select concat("kill ",id,";") from information_schema.processlist where command="Query" or command="Execute" into outfile "/tmp/kill.sql";'
$mysql_con -e "source /tmp/kill.sql" 2>> /etc/keepalived/logs/state_change.log
sleep 2 ###给kill一个执行和缓冲时间 slave_info() {
###检查从库状态
slave_stat=`mysql -u$REPL_USER -p$REPL_PASSWD -h$MASTER1 -e "show slave status\G"`
Master_Log_File=`echo $slave_stat | egrep -w "Master_Log_File" | awk '{print $2}'`
Read_Master_Log_Pos=`echo $slave_stat | egrep -w "Read_Master_Log_Pos" | awk '{print $2}'`
Relay_Master_Log_File=`echo $slave_stat | egrep -w "Relay_Master_Log_File" | awk '{print $2}'`
Exec_Master_Log_Pos=`echo $slave_stat | egrep -w "Exec_Master_Log_Pos" | awk '{print $2}'`
} slave_info
until [ $Read_Master_Log_Pos = $Exec_Master_Log_Pos -a $Master_Log_File = $Relay_Master_Log_File ]
do
echo -e "`date "+%F %H:%M:%S"` ----Relay_Master_Log_File=$Relay_Master_Log_File, Exec_Master_Log_Pos=$Exec_Master_Log_Pos is behind Master_Log_File=$Master_Log_File, Read_Master_Log_Pos=$Read_Master_Log_Pos, wait......" >> /etc/keepalived/logs/state_change.log
sleep 2
slave_info
done ###然后解除master1的read_only属性并打开keepalived服务
echo -e "`date "+%F %H:%M:%S"` ----set read_only = 0 on master1----" >> /etc/keepalived/logs/state_change.log
ssh $MASTER1 "$MASTER1_MYSQL_PATH/mysql -u$DB_USER -p$DB_PASSWD -e 'set global read_only = 0;' && /etc/init.d/keepalived start" 2>> /etc/keepalived/logs/state_change.log ###重启master2的keepalived服务,使VIP漂移到master1
echo -e "`date "+%F %H:%M:%S"` ----make VIP move to master1----" >> /etc/keepalived/logs/state_change.log
/etc/init.d/keepalived restart &>> /etc/keepalived/logs/state_change.log
echo "master2 keepalived restart,vip change to master1" | mail -s "master2 keepalived change to BACKUP" $U_EMAIL 2>> /etc/keepalived/logs/state_change.log
echo -e "\n--------------------------------------------------\n" >> /etc/keepalived/logs/state_change.log

在node2上执行脚本
chmod +x /etc/keepalived/manual_switch_to_master
[root@mysqlb keepalived]# sh /etc/keepalived/manual_switch_to_master

在node2上查看ip,发现vip不见了

在node2上查看read_only参数

在node1上查看vip,发现vip已经漂移过来了

手工切换完成!

八、总结

mm+keepalive的配置简单,相对于传统的主从架构,能实现比较简单的写库故障转移。
文章参考:https://www.cnblogs.com/ivictor/p/5522383.html

Mysql - 高可用方案之MM+Keepalived的更多相关文章

  1. MySQL高可用方案 MHA之四 keepalived 半同步复制

    主从架构(开启5.7的增强半同步模式)master: 10.150.20.90   ed3jrdba90slave: 10.150.20.97    ed3jrdba97 10.150.20.132 ...

  2. [转]MYSQL高可用方案探究(总结)

    前言 http://blog.chinaunix.net/uid-20639775-id-3337432.htmlLvs+Keepalived+Mysql单点写入主主同步高可用方案 http://bl ...

  3. [转载] MySQL高可用方案选型参考

    原文: http://imysql.com/2015/09/14/solutions-of-mysql-ha.shtml?hmsr=toutiao.io&utm_medium=toutiao. ...

  4. Heartbeat+DRBD+MySQL高可用方案【转】

    转自Heartbeat+DRBD+MySQL高可用方案 - yayun - 博客园 http://www.cnblogs.com/gomysql/p/3674030.html 1.方案简介 本方案采用 ...

  5. MySQL高可用方案MHA的部署和原理

    MHA(Master High Availability)是一套相对成熟的MySQL高可用方案,能做到在0~30s内自动完成数据库的故障切换操作,在master服务器不宕机的情况下,基本能保证数据的一 ...

  6. MySQL高可用方案--MHA部署及故障转移

    架构设计及必要配置 主机环境 IP                 主机名             担任角色 192.168.192.128  node_master    MySQL-Master| ...

  7. MySQL高可用方案-PXC环境部署记录

    之前梳理了Mysql+Keepalived双主热备高可用操作记录,对于mysql高可用方案,经常用到的的主要有下面三种: 一.基于主从复制的高可用方案:双节点主从 + keepalived 一般来说, ...

  8. 五大常见的MySQL高可用方案【转】

    1. 概述 我们在考虑MySQL数据库的高可用的架构时,主要要考虑如下几方面: 如果数据库发生了宕机或者意外中断等故障,能尽快恢复数据库的可用性,尽可能的减少停机时间,保证业务不会因为数据库的故障而中 ...

  9. mysql高可用方案MHA介绍

    mysql高可用方案MHA介绍 概述 MHA是一位日本MySQL大牛用Perl写的一套MySQL故障切换方案,来保证数据库系统的高可用.在宕机的时间内(通常10-30秒内),完成故障切换,部署MHA, ...

随机推荐

  1. 【游记】CSP J/S 2019 游记

    J 组 \(2:30\)开始, \(2:13\)还在酒店的我看了看手表...飞奔考场. T1 数字游戏 秒切. 下午某中学某大佬说可用线性基(%) T2 公交换乘 用单调队列思想,秒切. T3 纪念品 ...

  2. 使用ModelArts自动学习完成猫狗声音分类

    准备数据 点击下载猫狗声音数据集至本地: 解压,文件包结构大概如下图所示 data ├── test │ ├── cats │ │ ├── cat_20.wav │ │ ├── ...... │ │ ...

  3. shell du sh 文件大小输出

    按照文件大小升序输出结果: du -sh * | sort -h 如果要逆序输出,则: du -sh * | sort -hr

  4. 17.Django学习之django自带的contentType表

    通过django的contentType表来搞定一个表里面有多个外键的简单处理: 摘自:https://blog.csdn.net/aaronthon/article/details/81714496 ...

  5. ACM-ICPC 2018 焦作赛区网络预赛 G题 Give Candies

    There are NN children in kindergarten. Miss Li bought them NN candies. To make the process more inte ...

  6. 史上最全的iptables应用

    第14章 防火墙的使用 14.1 防火墙的概念 将不安全的网络流量信息进行隔离 14.2 防火墙的实现 14.2.1 硬件实现 思科,华为防火墙服务器 14.2.2 软件实现 iptables(cen ...

  7. Ceph 架构以及原理分析

    一.架构 Ceph在一个统一的系统中独特地提供对象,块和文件存储. Ceph高度可靠,易于管理且免费. Ceph的强大功能可以改变您公司的IT基础架构以及管理大量数据的能力. Ceph提供了非凡的可扩 ...

  8. Java并发编程系列-(4) 显式锁与AQS

    4 显示锁和AQS 4.1 Lock接口 核心方法 Java在java.util.concurrent.locks包中提供了一系列的显示锁类,其中最基础的就是Lock接口,该接口提供了几个常见的锁相关 ...

  9. iSensor App Kit 测试之 MT9V111 MT9M111 MT9D111

    iSensor App Kit 可以调试测试一切常规的sensor,对于ccusb20底板,可以直接兼容官哥所有的dvp接口的摄像头,分辨率从30w到1400w均没问题. 今天又测试了三款sensor ...

  10. flash存储器原理及作用是什么?

    flash存储器的工作原理 flash存储器又称闪存(快闪存储器),是一种电可擦可编程只读存储器的形式,是可以在操作中被多次擦或写,EEPROM与高速RAM成为当前最常用且发展最快的两种存储技术.计算 ...