本文目录导读:

- 通用脚本设计原则
- MySQL主从切换脚本(Python + pymysql)
- Redis哨兵/集群切换脚本(Bash + redis-cli)
- 通用健康检查 + API调用切换(Python + requests)
- 关键注意事项
编写监控数据容灾切换脚本需要根据具体的数据库类型、中间件和容灾架构来定制,下面提供几种常见场景的脚本思路和示例,包括 MySQL主从切换、Redis哨兵切换 以及 通用的健康检查+切换逻辑。
通用脚本设计原则
- 健康检查:定期检查主库是否可用(如端口、查询、复制状态)
- 故障判定:连续N次检查失败才认为故障,避免误判
- 切换逻辑:提升备库为主库,修改应用连接指向
- 通知告警:切换成功后通知运维人员
- 幂等性:避免重复切换造成脑裂
MySQL主从切换脚本(Python + pymysql)
场景:一主一从,主库故障时自动将从库提升为主库
#!/usr/bin/env python3
import pymysql
import time
import logging
import sys
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
# 配置信息
MASTER_HOST = '192.168.1.10'
MASTER_PORT = 3306
MASTER_USER = 'root'
MASTER_PASS = 'password'
SLAVE_HOST = '192.168.1.20'
SLAVE_PORT = 3306
SLAVE_USER = 'root'
SLAVE_PASS = 'password'
CHECK_INTERVAL = 5 # 秒
MAX_RETRY = 3 # 连续失败次数
def check_mysql_connection(host, port, user, password):
"""检查MySQL是否可达"""
try:
conn = pymysql.connect(host=host, port=port, user=user,
password=password, connect_timeout=3)
conn.ping()
conn.close()
return True
except Exception:
return False
def check_slave_status(host, port, user, password):
"""检查从库复制状态"""
try:
conn = pymysql.connect(host=host, port=port, user=user,
password=password, connect_timeout=3)
with conn.cursor() as cursor:
cursor.execute("SHOW SLAVE STATUS")
result = cursor.fetchone()
if result:
slave_io_running = result[10] # Slave_IO_Running
slave_sql_running = result[11] # Slave_SQL_Running
return (slave_io_running == 'Yes' and slave_sql_running == 'Yes')
conn.close()
return False
except Exception:
return False
def promote_slave_to_master():
"""将从库提升为主库"""
try:
conn = pymysql.connect(host=SLAVE_HOST, port=SLAVE_PORT,
user=SLAVE_USER, password=SLAVE_PASS)
with conn.cursor() as cursor:
# 停止复制
cursor.execute("STOP SLAVE")
cursor.execute("RESET SLAVE ALL")
# 设置只读关闭(让从库可写)
cursor.execute("SET GLOBAL read_only = OFF")
conn.commit()
conn.close()
logging.info("从库已提升为主库: %s:%d", SLAVE_HOST, SLAVE_PORT)
return True
except Exception as e:
logging.error("提升从库失败: %s", e)
return False
def switch_dns_or_config(new_master_ip):
"""修改应用配置或DNS指向新主库(示例为更新配置文件)"""
config_file = '/etc/app/db.conf'
try:
with open(config_file, 'r', encoding='utf-8') as f:
content = f.read()
# 替换旧IP为新IP(可根据实际配置调整)
old_ip = MASTER_HOST
new_content = content.replace(old_ip, new_master_ip)
with open(config_file, 'w', encoding='utf-8') as f:
f.write(new_content)
logging.info("配置文件已更新,新主库IP: %s", new_master_ip)
# 重启应用(可选)
# subprocess.run(['systemctl', 'restart', 'app-service'])
return True
except Exception as e:
logging.error("更新配置文件失败: %s", e)
return False
def main():
fail_count = 0
while True:
if check_mysql_connection(MASTER_HOST, MASTER_PORT, MASTER_USER, MASTER_PASS):
# 主库正常,重置计数器
fail_count = 0
logging.info("主库正常")
else:
fail_count += 1
logging.warning("主库不可达(第%d次)", fail_count)
if fail_count >= MAX_RETRY:
logging.info("主库故障确认,开始容灾切换...")
# 检查从库状态
if not check_slave_status(SLAVE_HOST, SLAVE_PORT, SLAVE_USER, SLAVE_PASS):
logging.error("从库状态异常,无法切换")
fail_count = 0
time.sleep(CHECK_INTERVAL)
continue
# 提升从库为主库
if promote_slave_to_master():
# 修改应用配置
if switch_dns_or_config(SLAVE_HOST):
logging.info("容灾切换完成,新主库: %s:%d", SLAVE_HOST, SLAVE_PORT)
# 发送告警(示例为打印日志)
# send_alert("MySQL容灾切换完成,新主库: %s" % SLAVE_HOST)
else:
logging.error("配置更新失败,请手动处理")
else:
logging.error("切换失败,请手动处理")
# 切换完成后可以退出或继续监控新主库
# 注意:这里只是演示,实际建议退出脚本并通知人工介入
break
time.sleep(CHECK_INTERVAL)
if __name__ == "__main__":
main()
Redis哨兵/集群切换脚本(Bash + redis-cli)
场景:Redis哨兵架构,主库故障时自动更新本地应用配置
#!/bin/bash
# Redis 容灾切换检测脚本
REDIS_MASTER="192.168.1.30"
REDIS_PORT=6379
SENTINEL_HOST="192.168.1.31"
SENTINEL_PORT=26379
APP_CONFIG="/etc/app/redis.conf"
CHECK_RETRY=3
FAIL_COUNT=0
check_master() {
redis-cli -h $REDIS_MASTER -p $REDIS_PORT PING > /dev/null 2>&1
return $?
}
get_new_master_from_sentinel() {
# 通过哨兵获取当前主库
redis-cli -h $SENTINEL_HOST -p $SENTINEL_PORT sentinel get-master-addr-by-name mymaster 2>/dev/null | head -1
}
switch_redis_config() {
local new_ip=$1
sed -i "s/^redis_host=.*/redis_host=$new_ip/" $APP_CONFIG
echo "更新Redis配置为新主库: $new_ip"
# 重启应用
systemctl restart app-service
echo "应用已重启"
}
# 主监控循环
while true; do
if check_master; then
FAIL_COUNT=0
echo "$(date) 主库正常"
else
((FAIL_COUNT++))
echo "$(date) 主库不可达 ($FAIL_COUNT/$CHECK_RETRY)"
if [ $FAIL_COUNT -ge $CHECK_RETRY ]; then
echo "主库故障确认,启动切换流程..."
# 获取新主库
NEW_MASTER=$(get_new_master_from_sentinel)
if [ -z "$NEW_MASTER" ]; then
echo "无法从哨兵获取新主库"
FAIL_COUNT=0
sleep 5
continue
fi
# 检查新主库是否可用
redis-cli -h $NEW_MASTER -p $REDIS_PORT PING > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "新主库 $NEW_MASTER 可用,更新配置..."
switch_redis_config $NEW_MASTER
echo "切换完成"
else
echo "新主库 $NEW_MASTER 也不可用,请人工介入"
fi
break
fi
fi
sleep 5
done
通用健康检查 + API调用切换(Python + requests)
如果需要对接云平台的API或自定义的切换服务,可以使用通用模式:
import requests
import time
import json
import logging
logging.basicConfig(level=logging.INFO)
class HealthCheck:
def __init__(self, endpoint, api_url, api_token):
self.endpoint = endpoint
self.api_url = api_url
self.api_token = api_token
self.fail_count = 0
self.max_retry = 3
def check_health(self):
"""通用健康检查,支持HTTP/TCP/ICMP"""
try:
r = requests.get(self.endpoint, timeout=5)
return r.status_code == 200
except:
return False
def trigger_switch(self):
"""调用容灾切换API"""
headers = {'Authorization': f'Bearer {self.api_token}'}
payload = {'action': 'failover', 'resource': 'db-primary'}
try:
r = requests.post(self.api_url, json=payload, headers=headers, timeout=10)
if r.status_code == 200:
logging.info("切换API调用成功: %s", r.json())
return True
else:
logging.error("切换API返回错误: %s", r.text)
return False
except Exception as e:
logging.error("调用切换API失败: %s", e)
return False
def run(self):
while True:
if self.check_health():
self.fail_count = 0
logging.info("服务正常")
else:
self.fail_count += 1
logging.warning("服务异常 (第%d次)", self.fail_count)
if self.fail_count >= self.max_retry:
logging.info("触发容灾切换...")
if self.trigger_switch():
logging.info("切换完成")
else:
logging.error("切换失败,请人工处理")
break
time.sleep(10)
if __name__ == "__main__":
checker = HealthCheck(
endpoint="http://10.0.0.1:8080/health",
api_url="http://switch-api.internal/failover",
api_token="your_token_here"
)
checker.run()
关键注意事项
| 注意点 | 说明 |
|---|---|
| 避免脑裂 | 切换前必须确认原主库已离线或停止服务 |
| 数据一致性 | 确认从库已完全同步(如MySQL的Seconds_Behind_Master为0) |
| 重试机制 | 避免单次延迟导致误判,建议连续3-5次失败才触发切换 |
| 日志记录 | 记录详细的切换时间、操作步骤、结果 |
| 通知告警 | 切换完成(或失败)后自动通知运维值班人员 |
| 回滚方案 | 切换脚本应保留切换前的配置备份,方便回滚 |
| 权限管理 | 脚本需要最小权限(如数据库只读检查、切换需写权限) |
| 幂等设计 | 防止重复触发切换,可用锁文件或状态标记 |
编写容灾切换脚本时,建议:
- 先实现监控检测,再增加切换逻辑
- 使用稳定成熟的客户端库(如pymysql、redis-py)
- 配置外部化,避免硬编码
- 测试环境充分演练再上线
- 监控脚本本身(比如脚本进程挂了要有告警)
根据你的实际架构(云原生、虚拟机、物理机)和数据库类型,选择对应的最佳实践即可。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播