这几周系统访问量也是居高不下,不出意外系统又出现瓶颈了,大量用户反馈判题结果响应太慢;经排查,又是关于SQL的问题
业务背景
一个类似于力扣在线做题的代码评测模块,用户提交判题任务后,后台会进行异步判题,前端会轮询判题结果,如下图
线上问题
大量的用户在前端提交后,一直都轮询不到判题结果。经代码排查,发现问题就出现在判题结果写库逻辑,耗时竟然有1s多。
而我们的判题结果写库 分为两个步骤
可以简单理解为
update xxxx where topic_id = xxx
//topic_id是索引
逻辑较多,可以理解为多次insert,和用户维度的update.
问题分析
查看方法调用日志, 发现 第一个步骤(更新这道题的提交数,正确率)占据了80%的耗时。
当我们 update xxxx where topic_id = xxx
时,MySQL会对topic_id 索引加行锁
,由于第一个步骤和第二个步骤又在同一个事务。
当高并发时,用户做题是多对一的关系,大量用户可能都在写一道题,造成题目ID的行锁竞争激烈,更新题目提交数、正确率的行锁在更新玩之后不会释放;还需等待第二步,将结果写库完后(等事务执行完后)。这样行锁的无效持有时间或者叫行锁的持有粒度就增加了。
解决问题
按问题解决,直接减小行锁的粒度。
将1、2两个步骤交换下顺序。交换后逻辑变为:
逻辑较多,可以理解为多次insert,和用户维度的update.
可以简单理解为
update xxxx where topic_id = xxx
//topic_id是索引
你可以简单的理解为 原先老逻辑是 先update,再insert。现在是先insert再update;这样行锁的持有粒度就降低了。
经此一役,判题结果写库的逻辑从原来的 400TPS直接拉高到2000多TPS!!!
总体
再总结一下,本篇通过线上判题结果的业务逻辑 分享SQL读写的调优小技巧,先insert再update,可以降低行锁的粒度,提高TPS。