秒杀场景模拟
秒杀场景
秒杀场景的负载特征对支撑系统的要求
特征是瞬时并发访问量非常高。
一般数据库每秒只能支撑千级别的并发请求,而 Redis 的并发处理能力(每秒处理请求数)能达到万级别,甚至更高。所以,当有大量并发请求涌入秒杀系统时,我们就需要使用 Redis 先拦截大部分请求,避免大量请求直接发送给数据库,把数据库压垮。
特征是读多写少,而且读操作是简单的查询操作。
库存查验操作是典型的键值对查询,而 Redis 对键值对查询的高效支持,正好和这个操作的要求相匹配。
秒杀的三个阶段
秒杀活动前
在这个阶段,用户会不断刷新商品详情页,这会导致详情页的瞬时请求量剧增。这个阶段的应对方案,一般是尽量把商品详情页的页面元素静态化,然后使用 CDN 或是浏览器把这些静态化的元素缓存起来。
秒杀活动开始
这个阶段的操作就是三个:库存查验、库存扣减和订单处理。(并发主要是在库存查验操作上)
库存查验:
为了支撑大量高并发的库存查验请求,我们需要在这个环节使用 Redis 保存库存量,这样一来,请求可以直接从 Redis 中读取库存并进行查验。
库存扣减:
需要使用Redis来执行;
因为放在数据库会出现两个问题:
- 额外的开销。主要是数据库与Redis同步问题需要额外开销。
- 下单量超过实际库存量,出现超售。由于数据库的处理速度较慢,不能及时更新库存余量,这就会导致大量库存查验的请求读取到旧的库存值,并进行下单。
订单处理:
这个步骤需要交给后端数据库处理;订单处理会涉及支付、商品出库、物流等多个关联操作,这些操作本身涉及数据库中的多张数据表,要保证处理的事务性,需要在数据库中完成。而且,订单处理时的请求压力已经不大了,数据库可以支撑这些订单处理请求。
前两个操作需要在Redis中保证原子性(利用lua脚本、分布式锁)。
秒杀活动结束后
在这个阶段,可能还会有部分用户刷新商品详情页,尝试等待有其他用户退单。而已经成功下单的用户会刷新订单详情,跟踪订单的进展。不过,这个阶段中的用户请求量已经下降很多了,服务器端一般都能支撑。
总结
秒杀系统是一个系统性工程,Redis 实现了对库存查验和扣减这个环节的支撑,除此之外,还有 4 个环节需要我们处理好。
- 前端静态页面的设计。秒杀页面上能静态化处理的页面元素,我们都要尽量静态化,这样可以充分利用 CDN 或浏览器缓存服务秒杀开始前的请求。
- 请求拦截和流控。在秒杀系统的接入层,对恶意请求进行拦截,避免对系统的恶意攻击,例如使用黑名单禁止恶意 IP 进行访问。如果 Redis 实例的访问压力过大,为了避免实例崩溃,我们也需要在接入层进行限流,控制进入秒杀系统的请求数量。
- 库存信息过期时间处理。Redis 中保存的库存信息其实是数据库的缓存,为了避免缓存击穿问题,我们不要给库存信息设置过期时间。
- 数据库订单异常处理。如果数据库没能成功处理订单,可以增加订单重试功能,保证订单最终能被成功处理。