MySQL双主复制
双主复制的原理
MySQL双主架构是一种比较简单的高可用架构,基于MySQL源生的主从同步实现,两个服务器互为对方的备库,同时两个服务器又都可以承接写任务。上层通过keepalived 实现vip 的自动切换,提供高可用服务。
双主复制的优缺点
- 优点
- 架构简单,基于MySQL自带的主从同步实现,部署容易
- 节省资源,同时可以满足普通业务的需要
- 互为备库,保证数据安全
- 缺点
- 基于MySQL主从同步,存在延迟,如果使用半同步复制,会对主库造成影响
- MySQL双主架构中没有冲突检测机制,容易出现主键冲突 ,造成同步失败
- 存在出现脑裂的风险
- 扩展复杂,如果主键在应用层使用mod 生成,每次扩展需要修改程序代码。
keepalived
keepalived 原理
keepalived 使用vrrp 协议,实现了路由功能的高可用(区别于Heartbeat或Corosync实现的的Service高可用)。
keepalived 主要又三个进程:主进程,vrrp 进程,healthcheckers 进程,
两个子进程都有watchdog 模块监控,其中vrrp 进程主要是负责vip和mac地址的映射,vip 的摘除等工作;checkers 进程主要负责的是系统的健康状态探测。
keepalived 可以使用shell 脚本对指定的进程或者服务进行简单的监控,如果资源或者服务不可用可以直接在脚本中停掉keepalived 主进程,来实现VIP的自动切换。
keepalived 部署
- 双主复制环境准备(略)
- 安装keepalived (rpm安装)
- 配置keepalived
主要的文件如下:
keepalived 配置文件:
主库的配置文件:
# cat /etc/keepalived/keepalived.conf
vrrp_script chk_mysql {
script "/etc/keepalived/check_mysql.sh"
interval 5
}
vrrp_instance VI_1 {
state 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 {
12.3.10.222/24
}
}
从库的配置文件,主从的差异主要是,从库在切换到主库之后,需要执行notify_master_mysql.sh ,该脚本的内容在后面
# cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
notify_master /etc/keepalived/notify_master_mysql.sh
virtual_ipaddress {
12.3.10.222/24
}
}
mysql 服务的检查脚本,keepalived 会执行脚本,检查MySQL的服务是不是正常,该脚本根据实际情况修改,主要的内容例如:检查MySQL服务,检查keepalived 是否异常,ping 网关检查等。此示例中简配。
# cat check_mysql.sh
#!/bin/bash
mysql_conn='mysql -uroot -p123456'
$mysql_conn -e "show master status " >>/dev/null 2>&1
if [ $? -ne 0 ];
then
killall keepalived
fi
备库切换为主库要进行的操作,主要是判断binlog的应用和开启读写功能。
# cat notify_master_mysql.sh
#!/bin/bash
###当keepalived监测到本机转为MASTER状态时,执行该脚本
change_log=/etc/keepalived/logs/state_change.log
mysql_con='mysql -uroot -p123456'
echo -e "`date "+%F %H:%M:%S"` -----keepalived change to MASTER-----" >> $change_log
slave_info() {
###统一定义一个函数取得slave的position、running、和log_file等信息
###根据函数后面所跟参数来决定取得哪些数据
if [ $1 = slave_status ];then
slave_stat=`${mysql_con} -e "show slave status\G;"|egrep -w "Slave_IO_Running|Slave_SQL_Running"`
Slave_IO_Running=`echo $slave_stat|awk '{print $2}'`
Slave_SQL_Running=`echo $slave_stat|awk '{print $4}'`
elif [ $1 = log_file -a $2 = pos ];then
log_file_pos=`${mysql_con} -e "show slave status\G;"|egrep -w "Master_Log_File|Read_Master_Log_Pos|Relay_Master_Log_File|Exec_Master_Log_Pos"`
Master_Log_File=`echo $log_file_pos|awk '{print $2}'`
Read_Master_Log_Pos=`echo $log_file_pos|awk '{print $4}'`
Relay_Master_Log_File=`echo $log_file_pos|awk '{print $6}'`
Exec_Master_Log_Pos=`echo $log_file_pos|awk '{print $8}'`
fi
}
action() {
###经判断'应该&可以'切换时执行的动作
echo -e "`date "+%F %H:%M:%S"` -----set read_only = 0 on DB2-----" >> $change_log
###解除read_only属性
${mysql_con} -e "set global read_only = 0;" 2>> $change_log
# echo "DB2 keepalived转为MASTER状态,线上数据库切换至DB2"|mail -s "DB2 keepalived change to MASTER"\
# slowtech@126.com 2>> $change_log
echo -e "---------------------------------------------------------\n" >> $change_log
}
slave_info slave_status
if [ $Slave_SQL_Running = Yes ];then
i=0 #一个计数器
slave_info log_file pos
###判断从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的时间限制为10s
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 1
slave_info log_file pos
else
echo -e "The waits time is more than 10s,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
slave_info log_file pos
echo -e "DB2's slave status is wrong,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