上周三凌晨两点,我的手机突然像疯了一样震动。接起来一听,是某电商平台的 DBA 老张,声音带着哭腔:“完了完了,核心库挂了,老板说再搞不定明天就让我走人。”我一边穿衣服一边问他情况,他说晚上有个促销活动,流量比预期大了三倍,数据库直接扛不住,CPU 飙到 100%,连接数爆满,业务彻底停摆。这种场景在数据库运维圈里太熟悉了,几乎每个干这行的人都经历过几次半夜被叫起来救火的时刻。老张那会儿已经折腾了快一个小时,重启、杀进程、加内存,能用的招全用了,但一放流量进来又崩。我让他先别慌,把慢查询日志调出来看看。

结果一查日志,问题就露馅了。有条 SQL 语句是普通的订单查询,但写法特别糙——用了 SELECT *,而且在 WHERE 条件里对日期字段用了函数,索引根本用不上。更要命的是,这个查询被前端页面每秒钟调了几百次,等于每个人都开着卡车在一条小巷子里挤。我让老张赶紧给这条 SQL 加个索引,同时在应用层加个缓存,别让用户每次刷新都去查库。前后不到十分钟,数据库压力从 100% 降到 30% 左右,业务恢复正常。老张长出一口气,跟我说以后再也不让开发随便写 SQL 了。其实这个案例很典型,很多数据库故障并不是系统本身的问题,而是 SQL 写得不好。一个索引能搞定的事,被折腾成了重大事故。
但说实话,SQL 问题还算好解决,真正让人头疼的是那些架构层面的坑。去年有个做社交 App 的客户找到我,说他们的数据库越来越慢,读写分离、分库分表都试过了,效果却越来越差。我看了他们的架构,发现问题在于读写分离做得太死板——所有读请求都走从库,但有些查询本可以走本地缓存,结果还是打到数据库上。而且他们的分库键选的是用户 ID,但业务场景里经常要按时间维度查全量数据,每次查询都得扫所有分库,比不分片还慢。我建议把实时性要求不高的查询切到 Elasticsearch 或 ClickHouse 上,别让 MySQL 扛所有活。同时把分库策略改成用户 ID 加时间戳的混合模式,这样大部分查询只需要命中两个分库就够了。改完后,数据库的 QPS 从峰值时的 5000 降到 800,响应时间从 2 秒缩到 50 毫秒。
还有一个案例让我印象深刻,是一家金融公司的数据库运维团队,他们用的是 Oracle,做了 RAC 集群,还配了 Data Guard 做灾备,看着挺唬人的。但有一次主库的磁盘阵列故障,系统自动切换到备库,结果备库因为配置参数没对齐,性能差得离谱,交易延迟从毫秒级变成秒级。更糟的是,他们平时只做了主备切换的演练,从未测试过切换后的性能表现。那次事故持续了将近两个小时,影响了大量用户的交易。事后复盘,问题出在运维人员太迷信高可用架构,以为有了 RAC 和 Data Guard 就万事大吉,忽略了配置一致性验证和性能基准测试。从此他们每个月都会做一次全链路压测,模拟主备切换后的实际负载,确保备库在关键时刻真的能扛住。
说到压测,就不得不提一个更常见的运维误区——过度依赖自动化工具。很多团队觉得上了自动化运维平台就一劳永逸,结果遇到突发情况反而手忙脚乱。我见过一个互联网公司,他们的数据库自动扩容脚本写得特别智能,能根据 CPU 和内存使用率自动加节点。但有一次半夜流量暴涨,脚本确实触发了扩容,却因为网络带宽没预留够,新节点加入集群时同步数据把带宽打满,导致现有节点的查询延迟飙升。运维人员半夜被叫起来,面对一堆告警和自动化工具的日志,根本不知道从哪下手。最后只能手动切流量、限流、分批扩容,才把问题稳住。自动化工具是帮手,但不是替身。你必须了解它的逻辑、边界条件和失败模式,否则工具越智能,越容易把人坑。
还有一种情况是数据库本身没问题,但业务方太“聪明”。之前有个做在线教育的客户,他们的运营团队为了拉新,搞了个“邀请好友送课时”的活动,逻辑是用户每邀请一个人,就更新一次数据库里的邀请记录。这个更新本身没问题,但运营设计了一个排行榜页面,每秒钟刷新一次,每次刷新都要扫全表算排名。活动上线半小时,数据库 CPU 直接爆表。我找到运营负责人,建议把排名改成每分钟更新一次,或者用 Redis 的 Sorted Set 来算。对方起初坚持说排行榜必须实时,我解释说实时其实就是用户点一下刷新按钮,延迟两秒和十秒对用户感受几乎没有区别。最终双方各让一步,改成每 10 秒用异步任务更新一次 Redis,数据库只负责记录邀请数据,排名计算全部交给 Redis。改完后,数据库负载从 90% 降到 10%,活动顺利跑完。
这些案例背后指向同一个核心问题:数据库运维不能只看数据库本身,必须把目光放到整个系统生态里。SQL 写得烂,是开发规范和代码审查不到位;架构设计不合理,是技术选型和容量规划有缺陷;运维工具不好用,是对工具的认知和演练不够。很多运维人员习惯把自己定位成“数据库的保姆”,出了事就去修,修完就完事。但真正有价值的运维,是在问题发生之前,通过日志分析、慢查询监控、容量预估等手段,提前把隐患找出来。比如定期分析慢查询日志,把执行频率高但效率低的 SQL 抓出来交给开发改;再比如根据业务增长曲线,提前预判扩容时间点,而不是等告警响了才行动。
我还想说一个容易被忽略的点——人。数据库运维往往是团队里压力最大、背锅最多的角色。业务正常时,大家觉得数据库就该稳定运行,没人会夸你;但一旦出问题,第一个被骂的就是 DBA。我认识的很多运维人员长期处于高度紧张状态,手机 24 小时不敢静音,出差都得背着电脑。这种高压环境下,很容易出现“救火型”运维——平时不巡检、不优化,等到出事才冲上去。救火救得再好,也改变不了火灾频发的现实。真正好的运维往往看起来很“懒”,因为他们把大部分精力花在了预防上。比如主动跟开发沟通,了解新功能对数据库的影响;定期做故障演练,确保每个预案都经过实战检验;编写脚本自动化重复性巡检工作,把时间省出来做更有价值的事。
说回那个凌晨被叫醒的老张。事故之后,我给他提了几个建议:第一,每周跑一次慢查询分析,把 TOP10 发给开发团队,要求限期整改;第二,所有新上线的 SQL 必须经过 DBA 审核,审核不通过不允许上线;第三,每个月做一次全链路压测,模拟各种极端场景。老张照做了,三个月后,他们团队的数据库故障率下降了 80%。他后来跟我说,以前总觉得运维就是背锅的,现在才发现,运维也可以很主动,甚至把锅都拆掉。数据库运维归根结底是四个字:防患于未然。你愿意花多少时间在“防”上,决定了你在“救”上要花多少精力。这个道理,干这行的人心里都清楚,但真正做到的人并不多。


