高并发、分布式数据库场景下需要注意的UPDATE SQL写法

看到此文,是否觉得体内洪荒之力爆发,饥渴难耐想吐槽、情不自禁想捐赠
本文为原创文章,尊重辛勤劳动,可以免费摘要、推荐或聚合,亦可完整转载,但完整转载需要标明原出处,违者必究。

支付宝微  信

业务模块

超级话题签到积分

具体场景

更新签到积分行为

integration_record - 每次产生积分行为的记录表。即每当用户产生积分行为(如:在超级话题内进行签到、转发和回复等行为),就会增加一条integration_record表的记录。表明该用户在该超级话题下通过哪些行为产生了多少积分增量。

integration - 每个用户的总积分表。即在每次写入一条integration_record记录后,会将积分增量累计加入到该表的总积分字段中。表明目前为止,该用户在该超级话题下通过积分行为一共获取的积分总数。

表的细节就不给出了(也没必要)。这两张表都以话题ID(topic_id)和用户ID(uid_id)进行联合索引。

具体问题

在某些情况下,某个用户所在某个话题下的所有integration_record记录的积分总和小于该用户在该话题下的integration表记录的总积分。

异常日志

通过检查线上异常日志发现,会偶发出现更新integration表记录返回0(更新影响记录行数为0)的情况。但理论上不应该出现这种情况。

分析

进而考虑是由于数据库主从同步的延迟导致。为什么这么猜测?看下面SQL:

update integration set integration = ? where topic_id = ? and uid = ?

上面为改动前(有问题)的SQL。其中第一个问号代表当前需要更新的积分总数。问题就出现在这个总数上。

这个总数是由当前用户产生的积分行为所增加分数与当前integration表中该用户的积分总数(读于从库)之和。

所以,在高并发的请求下,会出现同时待同步如下一些SQL:

update integration set integration = 1720 where topic_id = abc and uid = 12345678
update integration set integration = 1720 where topic_id = abc and uid = 12345678

之所以会出现如上第二条SQL的情况(integration值相同),就是因为在算当前integration字段值的时候,从库并没有完成第一条SQL的同步操作。

b6cc388dgw1f6mrf5l5m1j20c809gdgs

解决

所以,该问题就迎刃而解了。将SQL改成如下形式:

update integration set integration = integration + ? where topic_id = ? and uid = ?


文章来源:胡旭个人博客 => 【原】高并发场景下需要注意的分布式数据库同步问题

注:该文章涉及业务敏感信息均已被处理。如有风险,请联系站长以及时处理。

转载请注明出处,违者必究!


这是一篇原创文章,如果您觉得有价值,可以通过捐赠来支持我的创作~
捐赠者会展示在博客的某个页面,钱将会用在有价值的地方,思考中...


分类: MySQL, 技术, 编程 | 标签: , , , , , | 评论 | Permalink

发表评论

电子邮件地址不会被公开。