数据库高可用演练怎么做?稳态定义、注入点与中止条件

大家好,我是数据库小学妹👋 我踩过的坑,你别再踩。

前年我跟过一次真实的断电。系统是两个数据中心加一个异地灾备。方案评审我参与了,里面写的切换时间是 30 秒。每一步都写了,脚本也提前写好,平时跑得也稳。说实话,我当时觉得定期演练有点多余。

那天真断电了,从主中心挂掉到业务恢复,我们花了 40 分钟。

事后复盘,我把这 40 分钟拆开看。真正执行切换的那一段,只用了 90 秒。有 12 分钟,是两边的人在确认"到底是不是真的要切"。还有将近 20 分钟,耗在应用那边的连接重建和数据对账上。方案里写的 30 秒,只算了执行这一段。

那次之后我改了个习惯。任何一套高可用方案,我都要求它在演练里跑一遍。跑不过的方案,写得再漂亮我也不敢签字。以前做设计的时候我们有个词叫走查。设计稿画得再好,也得有人拿着它逐屏点一遍才算验收。数据库的高可用也一样,方案写得再全,也得跑一遍才知道行不行。

先定义什么叫"正常"

演练最容易漏掉的一步,是事先定义稳态。稳态就是一组能说清楚的指标。它们落在设定范围内,就说明系统正常。没有这个判据,演练做完你也不知道过没过。更糟的是,你可能压根没发现演练把系统弄坏了。

指标要从业务层选,不能只看数据库层。数据库各项都平稳,业务可能已经是坏的。举个我遇到过的情形。主库被切成只读之后,数据库进程好好的,连接数正常,复制也没断。但所有写入都失败了。只看数据库指标,这次演练的结论会是"没有影响"。我一般盯这几个。

指标 看什么 采集方式
错误率 业务接口的失败占比 应用监控
P99 延迟 尾部请求的耗时 应用监控
复制延迟 主从之间的位点差 心跳表或监控项
可写状态 当前主库能不能写 定期试写
数据一致性 主从关键表的行数与校验和 对账脚本

稳态基线要在注入前采一次,恢复后再采一次,逐项对比。

复制延迟这项要单独说。它是演练里最容易骗人的指标。Seconds_Behind_Master 这类字段有个坑。从库回放线程一停,它就显示成 NULL,看着像没有延迟。我更信心跳表。主库定时写一个时间戳,从库读出来算差值,这个数骗不了人。

sql 复制代码
-- 复制延迟:主库定时写心跳,从库读时间差
SELECT
  TIMESTAMPDIFF(SECOND, ts, NOW()) AS repl_lag_seconds
FROM heartbeat_log
ORDER BY ts DESC
LIMIT 1;

注入点清单

演练的核心动作是注入故障。故障不是随便制造的,每一种注入都对应一个要验证的能力。

注入点 手法 验证什么
进程崩溃 kill -9 数据库进程 编排层能不能在秒级发现并拉起
主库只读 打开 super_read_only 写入失败会不会被业务感知并正确反馈
复制中断 停掉从库回放线程 复制延迟告警会不会触发
网络分区 屏蔽对端数据库端口 脑裂防护有没有生效
磁盘抖动 限制 IO 带宽或加延迟 慢查询和连接堆积的扩散速度
连接打满 开满连接数 应用侧的超时与重试策略
时钟漂移 人为偏移系统时间 依赖时钟的判定逻辑会不会出错

几种常用的注入手法:

bash 复制代码
# 注入点一:数据库进程直接崩掉
kill -9 $(cat /var/lib/mysql/mysqld.pid)

# 注入点二:主库置为只读,模拟存储故障后的保护状态
mysql -e "SET GLOBAL super_read_only = ON;"

# 注入点三:掐断本机与对端的数据库端口,模拟网络分区
iptables -A INPUT  -p tcp --dport 3306 -j DROP
iptables -A OUTPUT -p tcp --sport 3306 -j DROP

网络分区这一项要多留个心。它验证的是脑裂防护。两个中心互相看不到。如果两边都认为自己该当主,数据就分叉了。演练时先把仲裁或投票机制的原理搞清楚,再动手。

爆炸半径一级一级放大

这是我最看重的一条纪律。演练的破坏力必须可控。方法是一级一级放大,上一级通过才允许进下一级。

级别 环境 允许的注入
一级 预发单实例 任意注入都可以
二级 预发集群 任意注入都可以
三级 生产从库 只做只读类注入,不碰主库
四级 生产主库 前三级的同一条剧本都跑过,且有完整回退

第一次演练绝不能在生产的核心主库上做。这句话我说过很多次,还是见过有人直接在主库上试 kill -9。他的理由是"我们有从库,切过去就行"。结果那套切换脚本从没在真实场景跑过。脚本里一个写死的 IP 没改,切换直接卡住。

剧本里必须先写中止条件

演练脚本不能只写"做什么",还要写"什么时候停"。要提前定三件事。哪几个指标一破就立刻回滚。谁有权喊停。喊停之后多久能回到稳态。这三个问题在演练开始前就要有明确答案,不能现场商量。

阈值我给个参考。错误率超过基线的两倍,P99 超过基线的三倍,复制延迟超过 30 秒,切换后关键表对账不一致。任何一条命中就中止,不犹豫。

喊停权要落到一个人头上,不能是"大家一起判断"。演练现场最怕的局面,是所有人都觉得该停,但没人开口。

数据安全的三条底线

演练前必须确认三件事。备份是真的能恢复的,不是备份任务显示成功。回退通道是通的,回退动作有人验过。演练产生的数据能回收,不会混进生产。第一件我要专门强调。备份任务的"成功"只代表文件写出来了。能不能恢复出来,是另一回事。我现在的做法是,演练开始前先在一个隔离环境里做一次真实恢复。恢复不出来的备份,等于没有备份。

一次完整的切换演练时间线

下面这张表是一场完整演练的时间线,从准备到恢复稳态。这是我每次评审都会拿出来看的东西。

时刻 动作 关注点
T-3 天 冻结变更,确认备份可恢复,通知业务方 变更窗口
T-30 分钟 采集稳态基线 基线快照
T-5 分钟 确认回退通道、中止权归属 人员到位
T-0 注入:主库进程 kill -9 告警是否触发、多久触发
T+40 秒 检测完成,编排层发现主库不可用 检测耗时
T+2 分钟 触发切换:VIP 漂移、连接串切换 切换开始时间
T+4 分钟 验证新主可写,应用侧连接重建 应用恢复时间
T+22 分钟 关键表对账:行数、校验和 数据差异
T+30 分钟 业务指标回到基线范围 实际 RTO
T+45 分钟 复盘,逐段核对耗时 问题清单

这张表最有用的地方,是把 RTO 拆成了几段。检测一段,决策一段,执行一段,应用恢复一段,数据校验一段。方案里写的那个数字,通常只覆盖执行那一段。

RTO 组成 方案里估的 实际测的 差在哪
检测 15 秒 40 秒 告警阈值设得太宽
决策 未计 12 分钟 没人被明确授权喊切
执行 30 秒 90 秒 脚本里有写死的 IP
应用恢复 未计 18 分钟 连接池没配自动重建
数据校验 未计 8 分钟 校验脚本要手工跑

差距的来源基本都在这里。方案里那个 30 秒没有错,它只是定义得比业务感知到的范围窄。

避坑清单

别在生产主库上做第一次演练。爆炸半径永远从最小的那一级开始,一级一级放大。上一级的剧本没跑通,就不许进下一级。

演练脚本要和变更一起做版本管理。不然半年后有人问起来,当时注入了什么、跑了哪些步骤,没人答得上来。脚本进了版本库,演练才有可复现性。

最后一条是我自己搞错的。我有一次演练报告写得挺漂亮,问题清单列了七条。然后就没有然后了。半年后另一次演练,前三条问题原封不动又出现了一遍。那次我才意识到,演练报告不进变更流程,就是一张废纸。现在我的做法是,演练发现的问题当天就建任务,指定负责人和期限。下次演练先复核上一批问题的闭环情况。

写在最后

高可用不是架构图画出来的,是演练出来的。一份没有发现任何问题的演练报告,本身就是最大的问题信号。系统是活的,配置会漂,脚本会旧,人也会换。每次都演练出"一切正常",只说明两件事,要么没注入到点上,要么判据设得太松。

我现在判断一套高可用方案靠不靠谱,不看架构图上有几个圈。我看它有没有一份带时间线的演练记录。有记录的,说明被真实检验过。没有的,说明还停在纸上。

你们的高可用演练,最近一次是什么时候做的?评论区聊聊。

我是数据库小学妹,帮你少走弯路少踩坑,咱们下篇见👋

相关推荐
zyseo81 小时前
谷歌SEO 站内搜索优化实战:把站内搜索词变成关键词金矿
java·服务器·数据库
qq_401700411 小时前
Qt 串口/网口通信:Hex 与 ASCII 编码转换深度指南
开发语言·数据库·qt
码爸1 小时前
多源异构数据库实时同步至 Doris
数据库·flink·scala
王大傻09281 小时前
堆叠注入(Stacked Queries Injection)详解:原理、利用与防御
服务器·网络·数据库·web安全·网络安全
ClouGence2 小时前
数据库迁移工具 CloudCanal v6.5.0.0 发布:新增 TDSQL PostgreSQL 多条链路,支持 MongoDB 双向同步
数据库·mysql·mongodb
AIGS0012 小时前
从814张表到250个本体:本体建模在做什么
数据库·excel·erp·数据中台·智能问数·本体语义·企业大脑
fengkai45453 小时前
十二、Redis -1
运维·数据库·redis
这个DBA有点耶3 小时前
Change Buffer深入:二级索引写入的隐形加速器与它的代价
数据库·mysql·代码规范
for_ever_love__3 小时前
MySQL 事务隔离级别讲透:MVCC、幻读与四个级别怎么选
java·数据库·mysql·事务·mvcc·不可重复读·幻读