了解数据竞争

  • 作者:KK

  • 发表日期:2017.2.9


先来了解一下问题的背景

假设有个抢购功能,商品只有1个,谁先抢到归谁,以下做法会有问题:

  1. 在数据库里保存了库存数量为1

  2. 抢购时间到了,用户A点击购买,select到库存为1,可以购买,然后为用户A增加财产记录,为了方便理解,我们假设由于一些原因,整个过程要10秒才能完成

  3. 可是用户B也相同时间购买,也是select到库存为1,可以购买,于是程序也为用户B增加财产记录,但实际上A正在购买,只是他的处理程序还没到10秒,库存还没被update为0而已

结果就变成了用户B也买到了,只要在用户A的运行过程中的间隙中同时去查库存


解决方案的关键词是队列

有很多队列产品可以用,比如redis缓存的list数据类型就能实现队列的功能

  1. 先往redis的list里存好可以被抢购的商品ID集合,比如队列里只有1个数据ID

  2. 用户A请求,程序从队列里取出一个值,list有这么一个特征:取值后,队列里的数据就不见了,被取出去了

  3. 用户B请求,发现队列是空的无法取值,买不到

我想这比较好理解吧


那如果数据库select库存后马上update为0呢?

不靠谱!因为select语句执行后,此时轮到编程语言进行运算,编程语言运算也可能要1毫秒时间,于是就在这1毫秒时间还没来得及update,别的请求又去select了库存

总结

做项目时,一定要分析一下哪些数据是要防止竞争的,特别是重要性、商业价值大的数据,判断如果出现竞争导致的后果严重的话,建议使用队列来保证它被A处理时,B无法处理

扩展思考的还有利用数据库的锁功能等等,但就上面的例子,用队列方案的人会更多,DB的表锁数据行锁不一定适用所有场合,特别遇到性能问题时

满足队列的主要特性

支持 .pop 这样的弹出操作,一旦弹出,数据就不再存于列表里面

支持 .push 这样的操作,一旦进入,其它进程/线程push前也能实时检测到里面已经有了值,可以防重复push