抢购秒杀时的高并发应该怎样优化

高并发下的数据安全
我们知道在多线程写入同一个文件的时候 , 会存现“线程安全”的问题(多个线程同时运行同一段代码 , 如果每次运行结果和单线程运行的结果是一样的 , 结果和预期相同 , 就是线程安全的) 。如果是MySQL数据库 , 可以使用它自带的锁机制很好的解决问题 , 但是 , 在大规模并发的场景中 , 是不推荐使用MySQL的 。秒杀和抢购的场景中 , 还有另外一个问题 , 就是“超发” , 如果在这方面控制不慎 , 会产生发送过多的情况 。我们也曾经听说过 , 某些电商搞抢购活动 , 买家成功拍下后 , 商家却不承认订单有效 , 拒绝发货 。这里的问题 , 也许并不一定是商家奸诈 , 而是系统技术层面存在超发风险导致的 。
超发的原因
假设某个抢购场景中 , 我们一共只有100个商品 , 在最后一刻 , 我们已经消耗了99个商品 , 仅剩最后一个 。这个时候 , 系统发来多个并发请求 , 这批请求读取到的商品余量都是99个 , 然后都通过了这一个余量判断 , 最终导致超发 。(导致了并发用户B也“抢购成功” , 多让一个人获得了商品 。这种场景 , 在高并发的情况下非常容易出现 。)
优化方案1:将库存字段number字段设为unsigned , 当库存为0时 , 因为字段不能为负数 , 将会返回false

抢购秒杀时的高并发应该怎样优化

文章插图
 
优化方案2:使用MySQL的事务 , 锁住操作的行
抢购秒杀时的高并发应该怎样优化

文章插图
 
3. FIFO队列思路
【抢购秒杀时的高并发应该怎样优化】那好 , 那么我们稍微修改一下上面的场景 , 我们直接将请求放入队列中的 , 采用FIFO(First Input First Output , 先进先出) , 这样的话 , 我们就不会导致某些请求永远获取不到锁 。看到这里 , 是不是有点强行将多线程变成单线程的感觉哈 。
然后 , 我们现在解决了锁的问题 , 全部请求采用“先进先出”的队列方式来处理 。那么新的问题来了 , 高并发的场景下 , 因为请求很多 , 很可能一瞬间将队列内存“撑爆” , 然后系统又陷入到了异常状态 。或者设计一个极大的内存队列 , 也是一种方案 , 但是 , 系统处理完一个队列内请求的速度根本无法和疯狂涌入队列中的数目相比 。也就是说 , 队列内的请求会越积累越多 , 最终Web系统平均响应时候还是会大幅下降 , 系统还是陷入异常 。
4. 文件锁的思路
对于日IP不高或者说并发数不是很大的应用 , 一般不用考虑这些!用一般的文件操作方法完全没有问题 。但如果并发高 , 在我们对文件进行读写操作时 , 很有可能多个进程对进一文件进行操作 , 如果这时不对文件的访问进行相应的独占 , 就容易造成数据丢失
优化方案3:使用非阻塞的文件排他锁
抢购秒杀时的高并发应该怎样优化

文章插图
 
5. 乐观锁思路
这个时候 , 我们就可以讨论一下“乐观锁”的思路了 。乐观锁 , 是相对于“悲观锁”采用更为宽松的加锁机制 , 大都是采用带版本号(Version)更新 。实现就是 , 这个数据所有请求都有资格去修改 , 但会获得一个该数据的版本号 , 只有版本号符合的才能更新成功 , 其他的返回抢购失败 。这样的话 , 我们就不需要考虑队列的问题 , 不过 , 它会增大CPU的计算开销 。但是 , 综合来说 , 这是一个比较好的解决方案 。
有很多软件和服务都“乐观锁”功能的支持 , 例如redis中的watch就是其中之一 。通过这个实现 , 我们保证了数据的安全 。
优化方案4:Redis中的watch
抢购秒杀时的高并发应该怎样优化

文章插图
 




    推荐阅读