一条SQL导致的生产事故
开发中经常使用sql,大家都知道sql一般要加条件,防止结果集过大撑爆内存和cpu;有些情况下(尤其是接手新项目时),代码中涉及的查询sql,建议发布生产之前要查询一下,看下结果集的量级(查询权限一般会开放),以防万一
事故现场
新需求的代码发布之后,公司一线用户在工作群中反馈系统使用中频繁报错,影响工作效率,需要开发赶紧解决
问题排查
- 查看日志发现,网关层调用目标服务,很多接口都是503
,除此之外并没有其他报错信息 - 然后查看线上服务状态,显示正常。排查中也发现,有的响应503的接口并没有在本次需求中变更过
- 由于新需求发布之前系统是正常的,所以初步怀疑是新发布的代码中可能有问题,排查重点转移到新发布的代码上(可以结合grafana等工具,可以很方便的看到机器的内存、CPU和JVM的GC情况)。
- 通过走查代码终于找出了问题:新需求的代码中有一处业务查询,sql的结果集非常大,一线业务人员只要在页面上点击了和该代码相关的功能按钮,后台服务就会将百万级的数据加载到内存中,机器的内存基本被打满,无法处理其他请求,导致调用该服务的接口大量503。一段时间后数据被处理完,系统又恢复正常,这也就印证了一线人员反馈页面是时好时坏。相关sql如下:
SELECT * FROM xxx_order WHERE execute_channel = '';
事故发生当时,此sql的结果集数量是130多万(SELECT COUNT(*) FROM xxx_order WHERE execute_channel = '';
),线上机器规格是2C4g,所以当页面触发到相关操作执行此sql时,是灾难性的
问题解决
定位到问题之后,为了不影响一线使用,先回滚了线上的版本。开发开始进行修复,测试验证之后,重新发布了新需求的代码
事故复盘
1.设计review
此次事故,后来发现是开发对需求理解偏差造成的,设计的时候是可以避免的
2.代码review
新需求的代码,开发者和leader都需要认真review,一般可以降低事故的发生概率
3.敬畏SQL
不管何种开发语言,开发者或多或少总会和sql打交道,代码中的sql都要做到心中有数。一般公司中都会有多套开发环境(dev/test/prod等),由于非生产环境的数据量可能没有达到生产的数据量级,有些问题是无法暴露的,这点需要注意,上生产前最好清楚生产上的数据量级