业务模块

超级话题签到积分

具体场景

更新签到积分行为

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 = ?

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

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

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

Share:

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.