业务模块
超级话题签到积分
具体场景
更新签到积分行为
表
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:
updateintegration
set integration = 1720 where topic_id = abc and uid = 12345678 updateintegration
set integration = 1720 where topic_id = abc and uid = 12345678
之所以会出现如上第二条SQL的情况(integration 值相同),就是因为在算当前 integration 字段值的时候,从库并没有完成第一条SQL的同步操作。
解决
将SQL改成如下形式:
update integration
set integration = integration + ? where topic_id = ? and uid = ?
文章来源:高并发、分布式数据库场景下需要注意的 UPDATE SQL 写法
注:该文章涉及业务敏感信息均已被处理。如有风险,请联系站长以及时处理。
转载请注明出处,违者必究!