如何设计一个秒杀系统(中)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 我们接着上篇继续讲,这篇主要讲一致性

一致性


秒杀系统中,库存是个关键数据,卖不出去是个问题,超卖更是个问题。秒杀场景下的一致性问题,主要就是库存扣减的准确性问题。


1 减库存的方式


电商场景下的购买过程一般分为两步:下单和付款。“提交订单”即为下单,“支付订单”即为付款。基于此设定,减库存一般有以下几个方式:


下单减库存。买家下单后,扣减商品库存。下单减库存是最简单的减库存方式,也是控制最为精确的一种。

付款减库存。买家下单后,并不立即扣减库存,而是等到付款后才真正扣减库存。但因为付款时才减库存,如果并发比较高,可能出现买家下单后付不了款的情况,因为商品已经被其他人买走了。

预扣库存。这种方式相对复杂一些,买家下单后,库存为其保留一定的时间(如 15 分钟),超过这段时间,库存自动释放,释放后其他买家可以购买。

能够看到,减库存方式是基于购物过程的多阶段进行划分的,但无论是在下单阶段还是付款阶段,都会存在一些问题,下面进行具体分析。


2 减库存的问题


2.1 下单减库存


优势:用户体验最好。下单减库存是最简单的减库存方式,也是控制最精确的一种。下单时可以直接通过数据库事务机制控制商品库存,所以一定不会出现已下单却付不了款的情况。

劣势:可能卖不出去。正常情况下,买家下单后付款概率很高,所以不会有太大问题。但有一种场景例外,就是当卖家参加某个促销活动时,竞争对手通过恶意下单的方式将该商品全部下单,导致库存清零,那么这就不能正常售卖了——要知道,恶意下单的人是不会真正付款的,这正是

“下单减库存” 的不足之处。


2.2 付款减库存


优势:一定实际售卖。“下单减库存” 可能导致恶意下单,从而影响卖家的商品销售, “付款减库存” 由于需要付出真金白银,可以有效避免。

劣势:用户体验较差。用户下单后,不一定会实际付款,假设有 100 件商品,就可能出现 200

人下单成功的情况,因为下单时不会减库存,所以也就可能出现下单成功数远远超过真正库存数的情况,这尤其会发生在大促的热门商品上。如此一来就会导致很多买家下单成功后却付不了款,购物体验自然是比较差的。


2.3 预扣库存


优势:缓解了以上两种方式的问题。预扣库存实际就是“下单减库存”和

“付款减库存”两种方式的结合,将两次操作进行了前后关联,下单时预扣库存,付款时释放库存。

劣势:并没有彻底解决以上问题。比如针对恶意下单的场景,虽然可以把有效付款时间设置为 10 分钟,但恶意买家完全可以在 10

分钟之后再次下单。


2.4 小结


减库存的问题主要体现在用户体验和商业诉求两方面,其本质原因在于购物过程存在两步甚至多步操作,在不同阶段减库存,容易存在被恶意利用的漏洞。


3 实际如何减库存


业界最为常见的是预扣库存。无论是外卖点餐还是电商购物,下单后一般都有个 “有效付款时间”,超过该时间订单自动释放,这就是典型的预扣库存方案。但如上所述,预扣库存还需要解决恶意下单的问题,保证商品卖的出去;另一方面,如何避免超卖,也是一个痛点。


卖的出去:恶意下单的解决方案主要还是结合安全和反作弊措施来制止。比如,识别频繁下单不付款的买家并进行打标,这样可以在打标买家下单时不减库存;再比如为大促商品设置单人最大购买件数,一人最多只能买 N 件商品;又或者对重复下单不付款的行为进行次数限制阻断等

避免超卖:库存超卖的情况实际分为两种。对于普通商品,秒杀只是一种大促手段,即使库存超卖,商家也可以通过补货来解决;而对于一些商品,秒杀作为一种营销手段,完全不允许库存为负,也就是在数据一致性上,需要保证大并发请求时数据库中的库存字段值不能为负,一般有多种方案:一是在通过事务来判断,即保证减后库存不能为负,否则就回滚;二是直接设置数据库字段类型为无符号整数,这样一旦库存为负就会在执行

SQL 时报错;三是使用 CASE WHEN 判断语句——

UPDATE item SET inventory = CASE WHEN inventory >= xxx THEN

inventory-xxx ELSE inventory END


业务手段保证商品卖的出去,技术手段保证商品不会超卖,库存问题从来就不是简单的技术难题,解决问题的视角是多种多样的。


4 一致性性能的优化


库存是个关键数据,更是个热点数据。对系统来说,热点的实际影响就是 “高读” 和 “高写”,也是秒杀场景下最为核心的一个技术难题。


4.1 高并发读


秒杀场景解决高并发读问题,关键词是“分层校验”。即在读链路时,只进行不影响性能的检查操作,如用户是否具有秒杀资格、商品状态是否正常、用户答题是否正确、秒杀是否已经结束、是否非法请求等,而不做一致性校验等容易引发瓶颈的检查操作;直到写链路时,才对库存做一致性检查,在数据层保证最终准确性。


因此,在分层校验设定下,系统可以采用分布式缓存甚至LocalCache来抵抗高并发读。即允许读场景下一定的脏数据,这样只会导致少量原本无库存的下单请求被误认为是有库存的,等到真正写数据时再保证最终一致性,由此做到高可用和一致性之间的平衡。


实际上,分层校验的核心思想是:不同层次尽可能过滤掉无效请求,只在“漏斗” 最末端进行有效处理,从而缩短系统瓶颈的影响路径。


4.2 高并发写


高并发写的优化方式,一种是更换DB选型,一种是优化DB性能,以下分别进行讨论。


4.2.1 更换DB选型


秒杀商品和普通商品的减库存是有差异的,核心区别在数据量级小、交易时间短,因此能否把秒杀减库存直接放到缓存系统中实现呢,也就是直接在一个带有持久化功能的缓存中进行减库存操作,比如 Redis?


如果减库存逻辑非常单一的话,比如没有复杂的 SKU 库存和总库存这种联动关系的话,个人认为是完全可以的。但如果有比较复杂的减库存逻辑,或者需要使用到事务,那就必须在数据库中完成减库存操作。


4.2.2 优化DB性能


库存数据落地到数据库实现其实是一行存储(MySQL),因此会有大量线程来竞争 InnoDB 行锁。但并发越高,等待线程就会越多,TPS 下降,RT 上升,吞吐量会受到严重影响——注意,这里假设数据库已基于上文【性能优化】完成数据隔离,以便于讨论聚焦 。


解决并发锁的问题,有两种办法:


应用层排队。通过缓存加入集群分布式锁,从而控制集群对数据库同一行记录进行操作的并发度,同时也能控制单个商品占用数据库连接的数量,防止热点商品占用过多的数据库连接

数据层排队。应用层排队是有损性能的,数据层排队是最为理想的。业界中,阿里的数据库团队开发了针对InnoDB

层上的补丁程序(patch),可以基于DB层对单行记录做并发排队,从而实现秒杀场景下的定制优化——注意,排队和锁竞争是有区别的,如果熟悉

MySQL 的话,就会知道 InnoDB 内部的死锁检测,以及 MySQL Server 和 InnoDB

的切换都是比较消耗性能的。另外阿里的数据库团队还做了很多其他方面的优化,如 COMMIT_ON_SUCCESS 和

ROLLBACK_ON_FAIL 的补丁程序,通过在 SQL 里加入提示(hint),实现事务不需要等待实时提交,而是在数据执行完最后一条

SQL 后,直接根据 TARGET_AFFECT_ROW

的结果进行提交或回滚,减少网络等待的时间(毫秒级)。目前阿里已将包含这些补丁程序的 MySQL 开源:AliSQL


4.3 小结


高读和高写的两种处理方式大相径庭。读请求的优化空间要大一些,而写请求的瓶颈一般都在存储层,优化思路的本质还是基于 CAP 理论做平衡。


5 总结一下


当然,减库存还有很多细节问题,例如预扣的库存超时后如何进行回补,再比如第三方支付如何保证减库存和付款时的状态一致性,这些也是很大的挑战。




相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
2月前
|
缓存 前端开发 NoSQL
如何设计一个秒杀系统?
本文详细介绍了秒杀系统的原理与设计方法,包括高性能、一致性、高可用性和可扩展性等方面的要求。文中通过前端和后端的设计方案,探讨了如何实现秒杀系统的高并发处理,例如页面静态化、限流、降级策略及缓存优化等。此外,还分享了实际项目中的库存系统架构设计经验,并提供了面试中如何回答此类问题的建议。
182 2
|
2月前
|
缓存 NoSQL 应用服务中间件
【开发系列】秒杀系统的设计
【开发系列】秒杀系统的设计
|
缓存 NoSQL 数据库
【高并发】秒杀系统设计思路
【高并发】秒杀系统设计思路
216 0
|
消息中间件 缓存 NoSQL
如何设计一个秒杀系统???
如何设计一个秒杀系统???
187 0
|
消息中间件 缓存 安全
秒杀系统(1)——秒杀功能设计理念
秒杀系统(1)——秒杀功能设计理念
185 0
|
消息中间件 缓存 JavaScript
如何设计一个秒杀系统
如何设计一个秒杀系统
|
缓存 NoSQL 安全
秒杀系统的设计思路
你好看官,里面请!今天笔者讲的是秒杀系统的设计思路。不懂或者觉得我写的有问题可以在评论区留言,我看到会及时回复。 注意:本文仅用于学习参考,不可用于商业用途,如需转载请跟我联系。
434 2
|
数据采集 域名解析 缓存
设计一个秒杀系统架构
对于秒杀架构的设计,需要遵循以下个原则: 东西不能超卖、 下单成功的订单数据不能丢失、 服务器和数据库不能挂 尽量不让机器人抢走 整体的思路 秒杀架构的设计方案就是一个不断过滤请求的过程,从系统架构层面来说,秒杀系统的分层思路如下。
179 0
|
消息中间件 缓存 运维
如何设计一个秒杀系统(下)
这里我们讲解最后一部分
324 0
如何设计一个秒杀系统(下)
|
数据采集 缓存 前端开发
如何设计一个秒杀系统(上)
秒杀大家都不陌生。自2011年首次出现以来,无论是双十一购物还是 12306 抢票,秒杀场景已随处可见。简单来说,秒杀就是在同一时刻大量请求争抢购买同一商品并完成交易的过程。从架构视角来看,秒杀系统本质是一个高性能、高一致、高可用的三高系统。而打造并维护一个超大流量的秒杀系统需要进行哪些关注,就是本文讨论的话题。
553 0
如何设计一个秒杀系统(上)