文档章节

如何解决秒杀的性能问题和超卖的讨论

 真爱2015
发布于 2016/08/15 11:51
字数 2038
阅读 82
收藏 8
点赞 0
评论 1

最近业务试水电商,接了一个秒杀的活。之前经常看到淘宝的同行们讨论秒杀,讨论电商,这次终于轮到我们自己理论结合实际一次了。

  ps:进入正文前先说一点个人感受,之前看淘宝的ppt感觉都懂了,等到自己出解决方案的时候发现还是有很多想不到的地方其实都没懂,再次验证了“细节是魔鬼”的理论。并且一个人的能力有限,只有大家一起讨论才能想的更周全,更细致。好了,闲话少说,下面进入正文。

 

一、秒杀带来了什么?

 

 

  秒杀或抢购活动一般会经过【预约】【抢订单】【支付】这3个大环节,而其中【抢订单】这个环节是最考验业务提供方的抗压能力的。

  抢订单环节一般会带来2个问题:

  1、高并发

  比较火热的秒杀在线人数都是10w起的,如此之高的在线人数对于网站架构从前到后都是一种考验。

  2、超卖

  任何商品都会有数量上限,如何避免成功下订单买到商品的人数不超过商品数量的上限,这是每个抢购活动都要面临的难题。

 

二、如何解决?

 

  首先,产品解决方案我们就不予讨论了。我们只讨论技术解决方案

1、前端

  面对高并发的抢购活动,前端常用的三板斧是【扩容】【静态化】【限流】

  A:扩容

  加机器,这是最简单的方法,通过增加前端池的整体承载量来抗峰值。

  B:静态化

  将活动页面上的所有可以静态的元素全部静态化,并尽量减少动态元素。通过CDN来抗峰值。

  C:限流

  一般都会采用IP级别的限流,即针对某一个IP,限制单位时间内发起请求数量。

  或者活动入口的时候增加游戏或者问题环节进行消峰操作。

  D:有损服务

  最后一招,在接近前端池承载能力的水位上限的时候,随机拒绝部分请求来保护活动整体的可用性。

 

2、后端

  那么后端的数据库在高并发和超卖下会遇到什么问题呢?主要会有如下3个问题:(主要讨论写的问题,读的问题通过增加cache可以很容易的解决)

  I: 首先MySQL自身对于高并发的处理性能就会出现问题,一般来说,MySQL的处理性能会随着并发thread上升而上升,但是到了一定的并发度之后会出现明显的拐点,之后一路下降,最终甚至会比单thread的性能还要差。

  II: 其次,超卖的根结在于减库存操作是一个事务操作,需要先select,然后insert,最后update -1。最后这个-1操作是不能出现负数的,但是当多用户在有库存的情况下并发操作,出现负数这是无法避免的。

  III:最后,当减库存和高并发碰到一起的时候,由于操作的库存数目在同一行,就会出现争抢InnoDB行锁的问题,导致出现互相等待甚至死锁,从而大大降低MySQL的处理性能,最终导致前端页面出现超时异常。

 

  针对上述问题,如何解决呢? 我们先看眼淘宝的高大上解决方案:

  I:  关闭死锁检测,提高并发处理性能。

  II:修改源代码,将排队提到进入引擎层前,降低引擎层面的并发度。

  III:组提交,降低server和引擎的交互次数,降低IO消耗。

  以上内容可以参考丁奇在DTCC2013上分享的《秒杀场景下MySQL的低效》一文。在文中所有优化都使用后,TPS在高并发下,从原始的150飙升到8.5w,提升近566倍,非常吓人!!!

  不过结合我们的实际,改源码这种高大上的解决方案显然有那么一点不切实际。于是小伙伴们需要讨论出一种适合我们实际情况的解决方案。以下就是我们讨论的解决方案:

  首先设定一个前提,为了防止超卖现象,所有减库存操作都需要进行一次减后检查,保证减完不能等于负数。(由于MySQL事务的特性,这种方法只能降低超卖的数量,但是不可能完全避免超卖)

update number set x=x-1 where (x -1 ) >= 0;

 

解决方案1:

  将存库从MySQL前移到Redis中,所有的写操作放到内存中,由于Redis中不存在锁故不会出现互相等待,并且由于Redis的写性能和读性能都远高于MySQL,这就解决了高并发下的性能问题。然后通过队列等异步手段,将变化的数据异步写入到DB中。

  优点:解决性能问题

  缺点:没有解决超卖问题,同时由于异步写入DB,存在某一时刻DB和Redis中数据不一致的风险。

 

解决方案2:

  引入队列,然后将所有写DB操作在单队列中排队,完全串行处理。当达到库存阀值的时候就不在消费队列,并关闭购买功能。这就解决了超卖问题。

  优点:解决超卖问题,略微提升性能。

  缺点:性能受限于队列处理机处理性能和DB的写入性能中最短的那个,另外多商品同时抢购的时候需要准备多条队列。

 

解决方案3:

  将写操作前移到Memcached中,同时利用Memcached的轻量级的锁机制CAS来实现减库存操作。

  优点:读写在内存中,操作性能快,引入轻量级锁之后可以保证同一时刻只有一个写入成功,解决减库存问题。

  缺点:没有实测,基于CAS的特性不知道高并发下是否会出现大量更新失败?不过加锁之后肯定对并发性能会有影响。

 

解决方案4:

  将提交操作变成两段式,先申请后确认。然后利用Redis的原子自增操作(相比较MySQL的自增来说没有空洞),同时利用Redis的事务特性来发号,保证拿到小于等于库存阀值的号的人都可以成功提交订单。然后数据异步更新到DB中。

  优点:解决超卖问题,库存读写都在内存中,故同时解决性能问题。

  缺点:由于异步写入DB,可能存在数据不一致。另可能存在少买,也就是如果拿到号的人不真正下订单,可能库存减为0,但是订单数并没有达到库存阀值。

 

三、总结

 

  1、前端三板斧【扩容】【限流】【静态化】

  2、后端两条路【内存】+【排队】

 

四、非技术感想

 

  1、团队的力量是无穷的,各种各样的解决方案(先不谈可行性)都是在小伙伴们七嘴八舌中讨论出来的。我们需要让所有人都发出自己的声音,不要着急去否定。

  2、优化需要从整体层面去思考,不要只纠结于自己负责的部分,如果只盯着一个点思考,最后很可能就走进死胡同中了。

  3、有很多东西以为读过了就懂了,其实不然。依然还是需要实践,否则别人的知识永远不可能变成自己的。

  4、多思考为什么,会发生什么,不要想当然。只有这样才能深入进去,而不是留在表面。

  ps:以上仅仅是我们讨论的一些方案设想,欢迎大家一起讨论各种可行方案。 

本文转载自:http://blog.csdn.net/zhoudaxia/article/details/38067003

共有 人打赏支持
粉丝 16
博文 103
码字总数 30911
作品 0
东城
加载中

评论(1)

如痴拉风
如痴拉风
79
如何解决秒杀的性能问题和超卖的讨论

如何解决秒杀的性能问题和超卖的讨论 最近业务试水电商,接了一个秒杀的活。之前经常看到淘宝的同行们讨论秒杀,讨论电商,这次终于轮到我们自己理论结合实际一次了。 ps:进入正文前先说一点...

fdhay
2016/09/08
71
0
mysql处理高并发,防止库存超卖

先来就库存超卖的问题作描述:一般电子商务网站都会遇到如团购、秒杀、特价之类的活动,而这样的活动有一个共同的特点就是访问量激增、上千甚至上万人抢购一个商品。然而,作为活动商品,库存...

凯文加内特
2016/01/25
636
0
关于并发、超卖处理的思路

做电商网站,经常会有各种秒杀和热门商品,所以高并发的处理一直是电商最重要的事情。这里记录下当初自己是如何处理的 写在前面: 1、本文设计到的并发处理均是针对纵向,不针对横向扩展,即...

卖小女孩的小火柴
2015/10/13
4.8K
6
从构建分布式秒杀系统聊聊Lock锁使用中的坑

前言 在单体架构的秒杀活动中,为了减轻DB层的压力,这里我们采用了Lock锁来实现秒杀用户排队抢购。然而很不幸的是尽管使用了锁,但是测试过程中仍然会超卖,执行了N多次发现依然有问题。输出...

小柒2012
05/30
0
0
限时抢码(秒杀)设计

做之前看了不少关于秒杀商品的文章,其中有一篇觉得很好:【问底】徐汉彬:Web系统大规模并发——电商秒杀与抢购 抢码的逻辑比秒杀商品要简单太多,虽然简单但是高并发的问题却还是存在的,有...

尚小胖
2016/02/26
244
0
用redis做一个简单的秒杀

下面是一个简单的下单操作 <?phpinclude "MMysql.class.php";$configArr=['host'=>,'port'=>,'user'=>,'passwd'=>,'dbname'=>,];$db = new MMysql($configArr);$sql="select * from sdb_b2c_......

蛋定龙
2015/10/05
4.7K
3
Redis常见的应用场景解析

Redis是一个key-value存储系统,现在在各种系统中的使用越来越多,大部分情况下是因为其高性能的特性,被当做缓存使用,这里介绍下Redis经常遇到的使用场景。 Redis特性 一个产品的使用场景肯...

IT米粉
2017/09/25
0
0
专访丁奇:阿里云即将开源AliSQL,针对秒杀优化

8月9日,在2016云栖大会·北京峰会上,阿里云宣布启动AliSQL数据库开源项目。预计在9月份邀请部分用户内测,预计在10月份,开发者阿里云Code平台和GitHub网站上可以下载AliSQL,感兴趣的读者...

局长
2016/09/02
7.9K
28
关于乐观锁问题

【关于乐观锁是否安全的问题】 问题如下: 前置条件:数据库中某张表设置字段version,表示更新操作的标志位,int类型。 操作过程:连接DB的connection_thread 先 select 需要update的某条记...

mark_rock
2017/02/27
374
7
热点推荐:秒杀系统架构分析与实战

1 秒杀业务分析 正常电子商务流程(1)查询商品;(2)创建订单;(3)扣减库存;(4)更新订单;(5)付款;(6)卖家发货 秒杀业务的特性(1)低廉价格;(2)大幅推广;(3)瞬时售空;(...

洋哥6
2016/04/14
359
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

idea tomcat 远程调试

tomcat 配置 编辑文件${tomcat_home}/bin/catalina.sh,在文件开头添加如下代码。    CATALINA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=7829" Idea端配......

qwfys
今天
1
0
遍历目录下的文件每250M打包一个文件

#!/usr/bin/env python # -*- utf-8 -*- # @Time : 2018/7/20 0020 下午 10:16 # @Author : 陈元 # @Email : abcmeabc@163.com # @file : tarFile.py import os import tarfile import thr......

寻爱的小草
今天
1
0
expect同步文件&expect指定host和要同步的文件&构建文件分发系统&批量远程执行命令

20.31 expect脚本同步文件 expect通过与rsync结合,可以在一台机器上把文件自动同步到多台机器上 编写脚本 [root@linux-5 ~]# cd /usr/local/sbin[root@linux-5 sbin]# vim 4.expect#!/...

影夜Linux
今天
1
0
SpringBoot | 第九章:Mybatis-plus的集成和使用

前言 本章节开始介绍数据访问方面的相关知识点。对于后端开发者而言,和数据库打交道是每天都在进行的,所以一个好用的ORM框架是很有必要的。目前,绝大部分公司都选择MyBatis框架作为底层数...

oKong
今天
13
0
win10 上安装解压版mysql

1.效果 2. 下载MySQL 压缩版 下载地址: https://downloads.mysql.com/archives/community/ 3. 配置 3.1 将下载的文件解压到合适的位置 我最终将myql文件 放在:D:\develop\mysql 最终放的位...

Lucky_Me
今天
2
0
linux服务器修改mtu值优化cpu

一、jumbo frames 相关 1、什么是jumbo frames Jumbo frames 是指比标准Ethernet Frames长的frame,即比1518/1522 bit大的frames,Jumbo frame的大小是每个设备厂商规定的,不属于IEEE标准;...

问题终结者
今天
2
0
expect脚本同步文件expect脚本指定host和要同步的文件 构建文件分发系统批量远程执行命令

expect脚本同步文件 在一台机器上把文件同步到多台机器上 自动同步文件 vim 4.expect [root@yong-01 sbin]# vim 4.expect#!/usr/bin/expectset passwd "20655739"spawn rsync -av ro...

lyy549745
今天
1
0
36.rsync下 日志 screen

10.32/10.33 rsync通过服务同步 10.34 linux系统日志 10.35 screen工具 10.32/10.33 rsync通过服务同步: rsync还可以通过服务的方式同步。那需要开启一个服务,他的架构是cs架构,客户端服务...

王鑫linux
今天
1
0
matplotlib 保存图片时的参数

简单绘图 import matplotlib.pyplot as pltplt.plot(range(10)) 保存为csv格式,放大后依然很清晰 plt.savefig('t1.svg') 普通保存放大后会有点模糊文件大小20多k plt.savefig('t5.p...

阿豪boy
今天
3
0
java 8 复合Lambda 表达式

comparator 比较器复合 //排序Comparator.comparing(Apple::getWeight);List<Apple> list = Stream.of(new Apple(1, "a"), new Apple(2, "b"), new Apple(3, "c")) .collect(......

Canaan_
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部