首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

记一次线上事故:并发引起数据库插入重复数据

事故描述:app 新用户注册成功后,打开首页,客户端请求后端接口为用户分配一个引导策略。由于客户端没有做全局缓存,在每次打开首页的同时,很多业务都请求策略这个接口,导致后端接口在处理并发请求的时候,没有加锁机制处理,在同一秒向数据库插入了多条数据。

每一功能上线后,的卢都能拍着胸脯说

可最后,

经过后端和客户端多次查看代码、讨论...(当时也很激烈)

得出了Bug点和解决方案

Bug点

客户端没有做全局缓存,频繁、同时请求同一接口。

后端数据库字段索引设计不合理(一个用户有且只有一个引导策略,应该加unique:唯一索引,而不是index:普通)。

后端业务代码处理不合理:先查缓存,再查数据库。没有考虑到负载均衡下网关请求延迟,数据库主从同步的延迟(可能主库插入了数据,但是从库中没有查询到数据,这样就会插入新的数据)。

毕竟线上事故,所以、、、即使在睡觉也得起来修复bug。在团队大佬的带领下,我们给出了几种解决方案

解决方案

客户端加全局缓存,只要接口正常返回数据,就缓存在客户端并增加过期时间,如果数据有变动再更新到缓存,这样避免了很多不必要的请求,减少服务器的很多压力。

数据库 guide表修改index索引为unique索引,但是现在表的数据近800万,更新索引需要一定的时间,可能引起其他未知问题。除非什么时候在“停服”期间修改索引,最终这个方案被抛弃掉。

后端增加锁机制:

先查询缓存,如果缓存没有查询数据再更新到缓存。

入库时增加锁处理,处理时间给1000毫秒,一旦数据库入库成功,立即更新到缓存,然后解锁。这一步非常关键,之前是解锁之后再更新缓存导致加锁没有效果,因为并发的时间间隔非常短。

比如A ,B两个请求在同一秒不同的毫秒来请求接口,A请求处理完了刚好解锁成功,这个时候还没来得及更新到缓存里,且B请求已经处理完了,又因为之前主从延迟原因在从库没有查询到数据,所以再次插入数据。

这几句值得我们仔细品味一番,

方案敲定开始撸coding...

请求策略 getUserGuide(int $userId)

分配策略并入库 allotGuide(int $userId)

至此,经过for模拟并发测试、上线观察了几天,数据不再有重复数据

Bug 总算了修复了,心里的一块石头也落下了,在没修复之前,一直惴惴不安,终日惶惶恐恐。

“人到中年不得已,保温杯里泡枸杞。皮糙肉厚还油腻,一顿能吃五斗米。”

最后附上加锁,解锁逻辑代码,如有更好的优化方案,欢迎来喷...

加锁

解锁

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20230305A014PU00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券