page contents

两个并发同时请求,修改数据导致数据有一次修改相同的数据

Pack 发布于 2020-03-16 14:40
阅读 1939
收藏 1
分类:数据库

attachments-2020-03-8xwsbGrD5e6f1f434a35e.png

加了悲观锁,

加了泄洪队列,

加了限流,

还会出现

最佳答案 2020-03-24 17:09

542
Pack
Pack

我想先了解下这个操作的逻辑

一般修改数据有两种逻辑

  1. 查找到某行数据,根据现有值在应用端做逻辑判断与计算,最后将结果使用一个update更新到数据库, 比如 扣款动作,先查询出余额,应用端判断余额是否足够本次扣款,如果够计算本次扣款之后的剩余余额,然后更新到数据库,如果不够,业务端报错
  2. 直接将逻辑转换成sql,通过sql更新到数据库;也是上面的案例,可以直接  update balance set money = money - currnet_consume where id = xxxx

两种方案的区别

方案1应用端能处理比较复杂的逻辑,并不限于余额校验,还有其他的一些规则等等,自由管控;缺点,如果查询与写入存在足够的时间差,并发的动作可能将数据修改异常,举个例子

客户余额10, a 动作扣款1, b动作扣款2,查询与扣款中间间隔 10秒(极端情况),那么

a动作,查到余额是 10, 本次扣款1,理论上扣款后余额为 9

假如 a做完之后再做b,那么b查到的余额是 9,扣款2,计算出结果为 7,写入数据库,这个结果和我们期望一致

那么假如a还没做完写入动作,b就开始了呢,b当时查到的 余额还是10,b计算了,扣款之后是 8,后面还存在两种可能,b先做完,那么余额为9, a先做完,那么余额为8,不管哪种结果,都不符合我们预期

这种时候需要控制的东西会比较多,如果是一个服务端,一般不存在这种情况;多个服务端做负载的时候,这种问题有可能发生,比较常规的处理办法是 查询时做行锁,同时不读脏数据

方案2,数据库本身对某行数据的修改都是串行的,不存在冲突。这种update也不关心原始值是多少,只关心变化。缺点是部分逻辑校验不好处理,比如余额为负这种情况,可能需要另外结合数据库的约束处理,应用端接收各种异常并作正确响应

请先 登录 后评论