欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  数据库

mysql 双机故障切换方案

程序员文章站 2024-01-28 22:51:16
...

情景描述:原来两台数据库server做主从,因为前端有redis帮忙扛着大部分读压力,故而数据库的压力并不大,所以我们也没做读写分离,从库单纯作为备份用。这样明

情景描述:

原来两台数据库server做主从,因为前端有redis帮忙扛着大部分读压力,故而数据库的压力并不大,所以我们也没做读写分离,从库单纯作为备份用。

这样明显有两个缺点

1、主库是单点

2、从库资源浪费

所以便想出了如下的方案:让从库作为主库的热备,具备自动故障切换(failover)功能。

基本思路:

将DB1和DB2做成主动被动模式的双主结构:DB1主动;DB2被动(通过read_only参数实现除root用户之外的只读特性),通过keepalived VIP对外,将VIP设置成原DB1的IP,保持改造过程对代码透明。

正常时,VIP在DB1,通过keepalived调用脚本定期检查mysql服务可用性(通过一个低权限用户连接mysql服务器并执行一个简单查询,根据返回结果来判定mysql是否可用)

若无法执行查询:

进一步通过 service mysql status 检查mysql服务是否正常:

若服务状态不正常,则尝试重启mysql,继续判断可用性:

若重启过后执行查询OK,则本次检查OK

若重启后仍无法执行查询,则应该关闭DB1的mysql,然后关闭DB1的keepalived,使VIP漂移到DB2,DB的keepalived察觉到自己进入master状态,通过notify_master机制触发脚本,完成将DB2的mysql从被动状态(只读)切换到主动状态(可读可写)的操作,并且发送通知邮件

若服务状态正常,则等待30s再次尝试,若仍然无法执行查询,则重启DB1的mysql,再尝试执行sql。若仍然无法执行成功,,则执行关闭DB1的 mysql、keepalived,并将mysql服务切换至DB2的操作。

以上是大致思路,具体实现看过下面的脚本,就会一目了然了。

DB1上keepalived 配置

! Configuration File for keepalived global_defs { notification_email { lijiankai@dmzj.com } notification_email_from mysql_HA@dmzj.com smtp_server 118.194.37.21 smtp_connect_timeout 30 router_id LVS_DEVEL } vrrp_script chk_mysql { script "/etc/keepalived/check_mysql.sh" interval 30 #这里我的检查间隔设置的比较长,因为我们数据库前面有redis做缓存,数据库一两分钟级别的终端对整体可用性影响不大。这也是我没有采用成熟的方案而自己搞了这一套方案的“定心丸” } vrrp_instance VI_1 { state MASTER interface em2 virtual_router_id 51 priority 100 advert_int 1 nopreempt #防止切换到从库后,主keepalived恢复后自动切换回主库 authentication { auth_type PASS auth_pass 1111 } track_script { chk_mysql } virtual_ipaddress { 192.168.1.5/24 } }

/etc/keepalived/check_mysql.sh脚本内容如下

#!/bin/sh ###判断如果上次检查的脚本还没执行完,则退出此次执行 if [ `ps -ef|grep -w "$0"|grep "/bin/sh*"|grep "?"|grep "?"|grep -v "grep"|wc -l` -gt 2 ];then #理论上这里应该是1,但是实验的结果却是2 exit 0 fi ###定义一个简单判断mysql是否可用的函数 function excute_query { mysql -uxxx -pxxx -e "show processlist;" 2>>/etc/keepalived/logs/check_mysql.err } ###定义无法连接mysql,且mysql服务状态异常时的处理函数 function service_error { echo -e "`date "+%F %H:%M:%S"` -----try restarting mysql-----" >> /etc/keepalived/logs/check_mysql.err /sbin/service mysql restart &>> /etc/keepalived/logs/check_mysql.err /sbin/service mysql status &>/dev/null if [ $? -ne 0 ];then ###若重启mysql服务后,仍不正常----关闭mysql----关闭keepalived echo -e "`date "+%F %H:%M:%S"` -----restart mysql failure-----" >> /etc/keepalived/logs/check_mysql.err echo -e "`date "+%F %H:%M:%S"` -----stop mysql-----" >> /etc/keepalived/logs/check_mysql.err /sbin/service mysql stop &>> /etc/keepalived/logs/check_mysql.err echo -e "`date "+%F %H:%M:%S"` -----now stop keepalived-----" >> /etc/keepalived/logs/check_mysql.err /sbin/service keepalived stop &>> /etc/keepalived/logs/check_mysql.err echo -e "\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" >> /etc/keepalived/logs/check_mysql.err fi } ###定义若mysql服务正常,但是无法执行查询的处理函数 function connect_error { echo -e "`date "+%F %H:%M:%S"` -----excute query error, but mysql service is ok, retry after 30s-----" >> /etc/keepalived/logs/check_mysql.err sleep 30 excute_query; if [ $? -ne 0 ];then echo -e "`date "+%F %H:%M:%S"` -----still can not connect to mysql-----" >> /etc/keepalived/logs/check_mysql.err service_error; excute_query; if [ $? -ne 0 ];then echo -e "`date "+%F %H:%M:%S"` -----stop mysql-----" >> /etc/keepalived/logs/check_mysql.err /sbin/service mysql stop &>> /etc/keepalived/logs/check_mysql.err echo -e "`date "+%F %H:%M:%S"` -----stop keepalived-----" >> /etc/keepalived/logs/check_mysql.err /sbin/service keepalived stop &>> /etc/keepalived/logs/check_mysql.err echo -e "\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" >> /etc/keepalived/logs/check_mysql.err fi fi } ###检查开始: mysql是否可连接 excute_query; if [ $? -ne 0 ];then ###检查服务状态 /sbin/service mysql status &>/dev/null if [ $? -ne 0 ];then ###若服务不正常 echo -e "`date "+%F %H:%M:%S"` -----mysql service error-----" >> /etc/keepalived/logs/check_mysql.err service_error; else ###若服务正常,但无法连接 connect_error; fi fi 注:脚本里关闭keepalived是在完全关闭mysql之后才执行,这样做的潜在缺点是:假如mysql服务关闭的时间过长,则直接导致整个切换过程过长,即服务终止时间过长 但是,之所以选择完全关闭DB1的 mysql再切换到DB2,是为了避免某些因素导致的数据不一致的发生