UPDATE wp_usermeta SET meta_value = meta_value - 80
WHERE user_id = 37 AND meta_key = 'points'
  AND meta_value >= 80;

两个请求几乎同时提交 80 分的兑换,原本 100 分的账号余额变成 -60,兑换单却生成了两笔。这类现象通常不是突然出现的,往前翻半天日志能看到苗头。

追下去是这么回事:代码先读余额再判断够不够,最后写回新值,读和写之间没有锁。两个请求读到同一份 100 分,各自都认为够用,于是都能扣成功。

这次卡住的地方就在「积分并发扣减」这一环,单独把它拎出来验证,别和其他改动混在同一次发布里。监控和日志的时间要对上,时区不一致会多绕很大一圈。

临时改动用注释写上日期和原因,过两周自己都记不清为什么这么写。

处理上按这个顺序:把判断和扣减合并成一条条件更新,用余额大于等于 80 做条件;看受影响行数,为 0 就直接返回余额不足,不再往下走兑换;兑换记录和扣分放同一个事务,失败一起回滚;用两个并发请求压一遍,余额不能出现负数。

确认没问题再收工,并发提交后余额停在 20,第二个请求提示余额不足,没有多出兑换单。

作者 9823189