高并发系统实战课
徐长龙
前微博架构师、极客时间架构师
11663 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 30 讲
结束语&结课测试 (2讲)
高并发系统实战课
15
15
1.0x
00:00/00:00
登录|注册

07|强一致锁:如何解决高并发下的库存争抢问题?

分享一些实践过程中常见但不容易被发现的精妙设计
通过原子操作+拆开库存方式实现库存方案时,如何减少库存为0后接口缓慢的问题?
不推荐多层锁及锁重入等问题
Redis是独立部署的,能够快速解决锁争抢问题
方法可以根据业务需要组合使用
使用Redis的Lua脚本实现多步骤库存操作
可以批量地快速实现库存扣减
记录或监控当前库存信息或版本号,对数据进行预操作
需要多次循环抢锁,性能较差
适合操作多个决策key才能完成争抢的情况
降低下单系统锁争抢压力
将多个库存合并成一个,改用rpop实现多个库存扣减
避免出现抢库存导致库存负数的情况
使用Redis中的list保存多张令牌来代表库存
不推荐使用数据库的行锁
将库存放在Redis中集中管理
在高并发修改的场景下使用互斥锁性能较差
使用锁或原子操作保护库存变量
多线程并行对同一个公共变量读写
思考题
总结
Redis Lua方式实现Redis锁
CAS乐观锁
自旋互斥超时锁
多库存秒杀
令牌库存
原子操作
并发库存锁
强一致锁:如何解决高并发下的库存争抢问题?

该思维导图由 AI 生成,仅供参考

你好,我是徐长龙。
这节课我会给你详细讲一讲高并发下的库存争抢案例,我相信很多人都看到过相关资料,但是在实践过程中,仍然会碰到具体的实现无法满足需求的情况,比如说有的实现无法秒杀多个库存,有的实现新增库存操作缓慢,有的实现库存耗尽时会变慢等等。
这是因为对于不同的需求,库存争抢的具体实现是不一样的,我们需要详细深挖,理解各个锁的特性和适用场景,才能针对不同的业务需要做出灵活调整。
由于秒杀场景是库存争抢非常经典的一个应用场景,接下来我会结合秒杀需求,带你看看如何实现高并发下的库存争抢,相信在这一过程中你会对锁有更深入的认识。

锁争抢的错误做法

在开始介绍库存争抢的具体方案之前,我们先来了解一个小知识——并发库存锁。还记得在我学计算机的时候,老师曾演示过一段代码:
public class ThreadCounter {
private static int count = 0;
public static void main(String[] args) throws Exception {
Runnable task = new Runnable() {
public void run() {
for (int i = 0; i < 1000; ++i) {
count += 1;
}
}
};
Thread t1 = new Thread(task);
t1.start();
Thread t2 = new Thread(task);
t2.start();
t1.join();
t2.join();
cout << "count = " << count << endl;
}
}
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

在高并发场景下,解决库存争抢问题是一个技术挑战。本文深入探讨了多种解决方案和优化方法。首先,通过并发库存锁的案例引出了多线程并行对同一变量读写可能出现的数据损坏问题,强调了使用锁或原子操作来保证变量在多线程并发情况下的准确性。随后,介绍了原子操作、将热门商品库存拆分放置在多个key中以及使用Redis的原子操作来保证库存的准确性。同时,指出了这些方法的局限性,如在处理业务故障时需要人工代码保障库存问题,以及在高并发情况下锁的自旋阻塞等待会浪费系统资源。此外,还讨论了多库存秒杀的设计调整和CAS乐观锁的实现方式。最后,介绍了使用Redis Lua脚本实现多步骤库存操作的方式。总的来说,本文通过具体案例和技术原理深入浅出地介绍了在高并发场景下解决库存争抢问题的方法和优化思路,对于需要解决类似问题的读者具有一定的参考价值。 文章通过具体案例和技术原理深入浅出地介绍了在高并发场景下解决库存争抢问题的方法和优化思路。从并发库存锁问题出发,介绍了原子操作、拆分热门商品库存、CAS乐观锁等多种解决方案,并指出了它们的局限性。最后,通过Redis Lua脚本实现多步骤库存操作的方式。文章内容丰富,对读者解决类似问题具有一定的参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《高并发系统实战课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(17)

  • 最新
  • 精选
  • Geek_8438e4
    面试原题,请指教:既然你们减库存用了redis,那如果redis挂了怎么办

    作者回复: 你好,目前为止我了解到的情况是,首先数据层有故障基本都是有损的,只是数据多少的问题。如线上数据层出现故障很多基础服务是不会马上就切换的,因为redis以及类似的数据服务都会有个探测过程,以防止只是一时业务查询导致的卡顿导致误判,像redis使用keepalive或哨兵时,也是通过多次ping检测来判断服务是否真正失联,多次延迟确认后才会切换主从,但是这样过程探测过程会过去五分钟以上,所以这个问题严谨一些的说他想问你能做哪些措施预防服务损坏,而不是让我们去讲完美无缺的提供服务强一致,秒杀肯定是要保证库存不要出错的,所以故障了多副本保证切换后数据十分精准的要求是无法满足的,行业出现这个情况是立即停止秒杀活动或者 降级服务 然后将请求放入队列后做相应补救。另外redis mysql等如果对一致性要求高的话,需要做的代价就特别大,每次切换主后从库都是需要同步主库所有数据进度,想要做好这里需要有性能很好的两个从库一起同步数据。所以做好前期流量预测,做好压测,预备措施可以准备一些但是多少都会丢一点

    2022-12-14归属地:内蒙古
    5
    11
  • 赵旭
    问题1:秒杀秒杀肯定是很快就没库存了,只要分片够均匀,在一个分片查不到就返回‘没有库存’或者‘参与用户太多,稍后再试‘呗,如果是要持续几分钟的才能抢完的,这种级别的流量一般也不用分片

    作者回复: 你好,这确实是一个知识点

    2022-12-07归属地:内蒙古
    5
  • 移横为固
    问题1:库存拆开后,当某个库存key数量为0后,删除这个key,减少下次接口调用需要查询的key数量。 在不考虑库存场景下,用锁的性能最高的就是不要有锁,如netty的reactive模型,把操作对象与线程进行绑定,避免争抢;还有ThreadLocal的线程副本。库存场景是对共享资源的操作,感觉只能用锁来同步

    作者回复: 你好,移横为固,这样如何保证不超卖呢?

    2022-11-09归属地:北京
    2
  • 张申傲
    这节课对于复习 Redis 很有帮助~

    作者回复: 你好,申傲,有任何疑问~多多交流!

    2022-11-08归属地:北京
    2
  • 李喂马
    请教个问题,全文讲的都是扣减库存的安全性和性能,但是秒杀系统经常会面临一些爬虫,在活动开始瞬间大量的爬虫请求导致库存被扣减完毕(这些些请求都的ip都是经过伪装的),导致真实用户无法购买到商品,面对这种情况老师有没有一些好的解决方案~

    作者回复: 你好,这里可以通过 js 对单次请求进行加密签名,并且保证每次秒杀接口入口流程和校验方式,每个活动都不同,这样能预防对方写一次脚本刷全站。 同时,这样做之后,他的爬虫必须使用我们的脚本计算才能正确请求,提高爬虫的门槛,因为这样对方就需要可编程浏览器才能模拟用户来抢,这个代价就会大很多。 另外要求必须真实绑定手机号用户才可以刷,限制单个用户指定时间内并发请求量,限制一个用户购买量,有未成交的不允许再下单,不用数字代表商品 id,让商品标识非数字连贯等多个组合。 总结一下就是不断提高技术要求和门槛,来降低他们抢的性价比。

    2023-01-04归属地:北京
    4
    1
  • dumpling
    rpop 支持一次性移出多个元素吗?哪个版本支持的特性?

    作者回复: 你好,dumpling,这里有点理解错误,rpop不支持多元素取出,原文:“改用 rpop 实现多个库存扣减。”是说使用rpop指令是解决方案中的一环主要特指令牌方式的实现方式,后续说解决多个库存是和他平级的环节。

    2022-11-27归属地:北京
    3
    1
  • 阿昕
    思考题:1.库存为了0接口缓慢,主要是因为需要遍历不同的key,可以把为0的key标记为删除,减少遍历次数;

    作者回复: 你好,如果是用incr这个方式,那么这里有个缺陷,服务端不知道这个key已经被删除了,他实际还是会decr一下这个操作~可能会出现负数

    2023-04-06归属地:浙江
  • Spoon
    1.拆分均匀地情况下,基本很快都被秒杀完,直接让用户重试就好 2.消除锁冲突,改成MQ顺序队列方案,其实有很多系统在进DB前都采用排队方式减少冲突

    作者回复: 有一瞬间极端情况,这种情况平时没什么,高峰期极端情况会雪崩,锁冲突这个方式缺点是消费过慢,并发高的几秒可能需要三小时消费完所有队列堆积

    2023-03-28归属地:浙江
    6
  • 花轮君
    针对于分片库存为0的场景,可以设定分片库存是否执行的标记,实时:当分片库存为0时原子化变更分片库存标记,定时:定时check 分片库存标记

    作者回复: 你好,这个方式缺点是退货后延迟一会儿才能拿到,好处是减少了不必要的尝试,是否合理要看我们业务需要

    2023-03-21归属地:上海
  • 普通熊猫 ଘ(੭ˊ꒳​ˋ)੭✧
    1. 我不认为基于redis锁的方案是一个好的秒杀方案, redis是一个AP系统, redis的分布式锁用于限流是可以的, 用于库存就有些危险了. 2. 如果让我设计的话, 主要做两点就够了. 一是限流: 基于总数的 (放10倍, 100倍于库存的流量进来都可以), 基于ip的, 基于user id的, 各种限流. 二是把下单请求放MQ中. 3. 放到MQ中的请求, 后面只需要架2台微服务慢慢消费就可以了, 后面所有扣减库存的操作都直接在DB里基于事务进行. 不需要架设大量的微服务, 因此也不需要考虑太多DB写压力的问题.

    作者回复: 你好,各有千秋,是否适合取决于业务规模:)

    2023-03-10归属地:北京
    3
收起评论
显示
设置
留言
17
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部