稳定性治理日志(四):短治长忧
本故事纯属虚构,如有雷同,纯属巧合。
第五次看见那道裂缝时,我知道系统又要崩溃了。
天花板的霉斑像一张逐渐展开的蛛网,昨夜又悄悄延伸了两毫米。水滴悬在裂缝末端,将落未落,像极了我监控屏上那个卡在99%的进度条。每次一到月末,办公室天花板都会渗出这种淡黄色的泪痕。
就在这时,手机开始震动,不是电话,而是接连不断的企业微信通知,像从深海浮现的一串急促信标。
十二条告警信息同时跳跃在屏幕上:
订单中心API超时率突破60%。
数据库CPU已飙升至90%。
我盯着大屏幕上密集交错的监控曲线,它们呈血红色,像子弹划破夜空后留下的伤口。本月第三次了——几乎一模一样的剧本,只有灾难的起点在不断变化。
鼠标滑过Grafana,数据像碎裂的河流涌向我。
终于,一条异常醒目的SQL语句跃入眼帘:
SELECT * FROM order_detail
WHERE user_id IN (/* 5000个ID */)
AND status IN (1,3,5,7,9,11,13,15)
ORDER BY create_time DESC
太阳穴开始剧烈跳动。
这条查询正在无情地扫描整个表,执行时间累计到187秒,在MySQL的processlist里,它如癌细胞般扩散,繁殖出三十多个副本。
“缓存呢?”我问,声音像钝刀划过砂纸。
“缓存…“小康苦笑,声音几乎不可闻,”redis内存爆了。”
隔壁小组发布的新功能,成了压垮系统的最后一根稻草。
办公室里,空气仿佛凝结。黄工靠在椅背上,指尖无声地敲击着保温杯的塑料边缘。我们都在等待,像等待冰层破裂前那一声脆响。
终于,他开口了。
“三个问题。”
他放下杯子,声音冷静到近乎残酷:
“第一,IN查询没有分页;第二,status字段缺失索引;第三…”
他切换到Redis内存分析界面,”他们把一个七百多万长度的对象直接塞进了Redis。”
正在发布新功能的小程赶了过来过来:”没想到第三方会推这么多数据过来…”
声音还没落下,就被黄工打断:
“每一个疏忽的小点都有可能是性能的坟墓。”
我注意到他说到“坟墓”时,右手下意识摸了摸左腕。那儿有一道浅浅的疤痕,是去年十一月之后留下的,静脉输液的针眼位置。
我记得,在需求评审会上,曾经有人提过缓存对象尺寸控制的风险。大家都同意,但事后却没有人跟踪与落地。
临时处置方案迅速制定并执行:
- 增加status字段的联合索引。
- 重写SQL,强制分页。
- 回滚大key相关代码。
“系统不会突然崩溃。”
他调出90天响应时间的趋势图,冰冷的数据斩断了所有借口——接口响应从最初的200ms,缓慢而无声地劣化到1.8秒。
黄工指着屏幕,声音低而缓慢,像在宣读一纸判决,”它早就开始尖叫了,只是没有人愿意听。”
随着应急措施的生效,十分钟后,系统各项指标开始缓慢恢复,像冰原上新生的一缕风。
我走到饮水机旁,接了一杯水。水声在寂静中格外清晰。远方,大厦工地的塔吊在晨雾中若隐若现。
黄工不知何时站到了我身旁,也端着一杯水。他的声音像从地底深处传来:
“知道为什么DBA都短命吗?”
他没等我回答,继续说道:
“每一条慢SQL,都是往血管里塞进去的血栓。”
他顿了顿,目光穿透了玻璃窗,像在看一片见证过无数死亡的战场。
“短期方案只是止血,真正的病灶在…”
他看着我。
“在不合理的架构?”我试探着回答。
黄工摇头,嘴角勾出一个几乎不可察觉的弧度。
“在人性里的懒惰。”
他说得极轻,但字字如锤击骨骼。
“技术债不会自己消失,只会沉淀、结晶,变成系统里的金刚石,划破所有试图修复它的手。”
那一刻,我突然明白了。稳定性治理,并不是维护什么技术奇迹,而是一项项技术债治理,是在不断与人的惰性和侥幸作战。
“从今天开始,每天第一件事,巡查系统。慢SQL分析,监控曲线巡检,一条一条过。”
我默默点头,将水杯放在桌边。
一滴冰冷的冷凝水从天花板滴落,沿着我的后颈滑下,带着细微刺痛感。
抬头望去,天花板上,一小片暗色的水渍正在缓慢扩散,像即将撕开的裂隙。
它静静地蔓延着,正如我们脚下系统里的裂痕——无人察觉时,灾难已经悄然开始。