自从踏进互联网运维这个行当,就无时不刻不在为高可用费神。nginx、tomcat、缓存、队列、数据库,每个环节高可用的最基本要求是避免单点故障,能够自动failover。mysql的高可用方案说起来很多,但真正想在你家的生产环境大面积使用,发现这个有缺点、那个不完美。之前用过MHA一段时间,实现相对较复杂(可能因为我没搞过perl),加上作者不再更新,总担心误切、脑裂……,至于高大上的PXC\MGR,前者有明显的缺陷,后者还较新,缺少大规模的经验。
我的目的:小公司只有1-2个DBA,不可能有人一直守着,甚至某些时候出门在外,1-2个小时内无法上网处理问题,这时主挂了,一定要能自动切换,否则公司停摆。。。
我的要求:简单实用、可靠、主从不要轻易切、尽最大可能不脑裂、不丢事务。
网上常见的一个简单的方案是:mysql双主+keepalived。猛一看很完美:mysql两个库可写、keepalived任意切换,像管理无状态服务一样自在……,其实这种方案有很明显的缺点:主从同步有延迟时,如果发生切换,发生数据错乱的概率太高(对于很多系统来说,宁可宕机几分钟,也不要产生大量脏数据)
我的方案是对mysql双主+keepalived的方案的改进:mysql主从半同步复制+keepalived+第三方程序和数据辅助切换判断
主要特点:
1、从库只读,切换为主时才可写
2、keepalived不配置virtual_ipaddress,由notify脚本实现IP漂移
3、把主从切换置于“重量级”的等级,不要轻易切换:
keepalived监控连续失败2分钟再切换(如mysqld宕机后能被mysqld_safe重启,则不切换);
切换脚本要判断很多逻辑,确保不会人为失误导致切换,也确保从库不满足切换条件时不会切换。
4、切换是单向的,从切为主后,需要重建部署主从环境
5、说到MHA,很多人会担心脑裂问题,keepalived网络环境比mha简单,在确保主从两节点在一个二层网络时,即便keepalived发生了脑裂(即双节点都有VIP),ARP广播可以确保只有一个节点的VIP是可以对外服务的,因此脑裂导致脏数据的可能性接近为零。
项目名称:mkf (mysql keepalived failover)
脚本地址:https://github.com/meishd/mkf
架构图:
3.1. 心跳表
所有的数据库有一个心跳表dbadmin.heartbeat(id,create_time),JOB每秒更新create_time,该表是我们用来监控从库同步延迟的;这里用这个表判断从库是否满足切主条件。
3.2. python数据抽取与dbfailover
dbfailover位于独立的实例,包括两张表:主库配置表master_info,切换仲裁信息表master_arbit_info
master_info自动:
master_arbit_info字段:
python程序读取master_info表初始化每个主库的连接池,定期(10秒)检测连接池、失效重连,定期(每秒)抽取信息到master_arbit_info。
3.2. 主节点控制
主连续宕机120秒(interval 10,fall 13)才切换,自动重启不切换,避免轻易切换;
主节点通过notify_backup控制VIP删除,降低运维操作的风险、避免对主库正常使用的影响
3.3. 从节点控制
从节点通过notify_master控制从升为主,目的是尽最大可能确保从库不丢事务才能升为主,否则宁可不切换。
以下是必须按顺序满足的条件,否则终止操作:
满足上述条件后,从库正式执行切换操作:
从库正常切为主后的日志:
# tail -20 notify_master.log
20200318 14:42:48 notify master begin...
20200318 14:42:48 1. master is offline
20200318 14:42:48 2. master_arbit_info records within 120 seconds: 0
20200318 14:42:48 3. master_arbit_info records within 180 seconds: 52
20200318 14:42:48 4. master_arbit_info last semi status: 1
20200318 14:42:48 5. master_arbit_info last heartbeat_time after create_time: 0
20200318 14:42:48 6. slave exec log lag behind read log: 0
20200318 14:42:48 7. heartbeat lag between master and slave: 1
20200318 14:42:49 switch to master
Warning: Using a password on the command line interface can be insecure.
20200318 14:42:49 add vip
ARPING 10.40.12.104 from 10.40.12.104 eth1
Sent 1 probes (1 broadcast(s))
Received 0 response(s)
ARPING 10.40.12.254 from 10.40.12.104 eth1
Unicast reply from 10.40.12.254 [84:D9:31:9F:29:75] 1.416ms
Sent 1 probes (1 broadcast(s))
Received 1 response(s)
20200318 14:42:51 notify master end
# more slave_status.log.20200318_144248
Warning: Using a password on the command line interface can be insecure.
*************************** 1. row ***************************
Slave_IO_State: Reconnecting after a failed master event read
Master_Host: 10.40.12.101
Master_User: lbadmin
Master_Port: 3366
Connect_Retry: 60
Master_Log_File: mysql-bin.000004
Read_Master_Log_Pos: 74770
Relay_Log_File: relay-bin.000005
Relay_Log_Pos: 74980
Relay_Master_Log_File: mysql-bin.000004
Slave_IO_Running: Connecting
Slave_SQL_Running: Yes
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 74770
Relay_Log_Space: 137461
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 2003
Last_IO_Error: error reconnecting to master '[email protected]:3306' - retry-time: 60 retries: 3
Last_SQL_Errno: 0
3.4. 测试用例
测试对象 | 判断条件 | 异常条件模拟 |
主库 notify_backup.sh |
1.keepalived启动时间超过60秒 | 每次新启动keepalived时会短暂进入backup状态,再进入master状态,触发异常 |
2.mysql端口不通 | 主节点keepalived.conf将notify_backup误写为notify_master,启动后触发异常 | |
从库 notify_master.sh |
1.主服务器mysql端口不通 | 先起从节点keepalive,导致从进入master状态,触发异常 |
2.master_arbit_info无120秒内的数据 | keepalived监测脚本判断时间不足120秒,如interval 10,fall 5 | |
3.master_arbit_info有180秒内的数据 | python数据抽取程序挂了 | |
4.master_arbit_info最后一条semi_status=1 | 做大数据量的DML,或者从库stop slave一段时间后再起,确保半同步降级2分钟以上,关主库 | |
5.master_arbit_info最后一条heartbeat正常更新 | 主库关闭heartbeat job | |
6.从库回放所有日志,如未完成循环等10分钟 | 主库DDL大表,DDL完成后关主库 | |
7.0<=(从库心跳-master_arbit_info最后心跳)<=1 | 不好用实际场景模拟,关主库后删除master_arbit_info表2条最新的数据 |
4.1. python程序部署
4.2. 主从库环境部署
主从服务器上安装部署keepalived
主节点配置文件:keepalived_master.conf,脚本文件:notify_backup.sh
从节点配置文件:keepalived_slave.conf,脚本文件:notify_master.sh
其中keepalived_master/slave.conf修改IP信息,notify_backup/master.sh修改文件头的变量
4.3.启停keepalived顺序
启动时先起主节点keepalived,再起从节点keepalived;
如果先起从后起主,不会对线上业务有影响,但是要重启从节点的keepalived,确保主进入master状态。
4.4. failover后的操作
停止两节点的keepalived;
如果老主服务器可修复,查看Executed_Gtid_Set信息,与从节点切换为主时记录的slave status比较,确认是否有事务丢失;
如有事务丢失则解析binlog,与研发一起处理数据问题,无则搭建新的主从,调整keepalived配置;
更新master_info中的IP信息,python程序会在10秒内自动更新连接池配置。
==== 完,即将在生产环境部署,欢迎拍砖 ====