做后端开发的,谁没遇到过几次线上故障呢。有的故障很简单,重启一下就好了。有的故障却很折腾,能让你熬一整个通宵。上个月我们就遇到了一次比较棘手的线上故障,整个排查过程花了六个多小时,现在想起来还记忆犹新。

那天是周三,下午三点多,运维群里突然有人说网站访问变慢了。我一开始没太在意,以为是正常的流量波动。但过了十分钟,又有人说部分接口超时了,我才意识到事情可能没那么简单。

登录监控系统一看,数据库的CPU已经跑到了百分之九十多,连接数也快满了。平时这个时间点数据库的CPU也就百分之三四十,明显不正常。我赶紧去看慢查询日志,发现有一条SQL语句的执行时间特别长,而且执行频率很高。

这条SQL是一个列表查询,看起来很普通,就是根据几个条件分页查询数据。但仔细一看,where条件里有一个字段没有建索引,而且这个字段的区分度很低。数据量小的时候没问题,现在数据量上来了,每次查询都要全表扫描,自然就慢了。

找到原因之后,解决方案很简单,给那个字段加上索引就行了。但问题是,这张表已经有几百万条数据了,加索引的过程中会锁表,影响线上业务。我们不敢直接在生产环境加,只能先想办法缓解。

临时的办法是把这个查询改成走另外一个有索引的字段,虽然查询结果不是特别精确,但至少能让数据库先缓过来。改完代码上线之后,数据库的CPU果然降下来了,网站也恢复了正常。

但这只是临时方案,根本问题还没解决。我们决定在凌晨流量低的时候加索引。凌晨两点,我和运维一起操作,先在从库上加索引,加完之后切换主从,再在原来的主库上加。整个过程很顺利,没有出什么问题。加完索引之后,那条查询的执行时间从几秒降到了几毫秒。

本以为事情就这样结束了,没想到第二天又出了问题。加完索引之后,数据库的CPU是正常了,但内存使用率却一直在涨。一开始我们以为是正常的缓存,没太在意。但到了下午,内存使用率已经到了百分之九十,开始触发告警了。

这次排查花了更长的时间。我们看了数据库的配置,看了连接数,看了慢查询,都没发现明显的问题。后来还是一个有经验的同事提醒,会不会是加索引之后执行计划变了,导致某些查询走了错误的索引。我们一查,果然如此。有一条查询本来应该走A索引,加了新索引之后优化器选择了B索引,而B索引的区分度更差,导致查询效率反而降低了。

解决办法是用force index强制指定索引,或者优化SQL语句的写法。我们改了几条SQL,数据库的内存使用率就慢慢降下来了。

这次故障给我们的教训很深。首先是上线前一定要做好压测,不能想当然地觉得没问题。其次是加索引这种操作一定要谨慎,不是加了索引就一定好,有时候反而会带来新的问题。最后是监控一定要完善,很多问题如果能早一点发现,就不会发展成严重的故障。

故障解决之后,我们做了一次完整的复盘。把整个过程记录下来,从问题发现到临时处理,再到根本解决,每一步都写得很清楚。这份复盘文档后来成了团队的培训材料,新同事入职的时候都会看一看。

做技术这行,故障是不可避免的。重要的不是不出故障,而是出了故障之后能不能快速解决,并且从中学到东西。每一次故障都是一次成长的机会,只要你认真对待,就能变得更强。