Redis分段锁,如何设计?

问题场景:热点库存扣减问题

秒杀场景,有一个难度的问题:热点库存扣减问题。

  • 既要保证不发生超卖

  • 又要保证高并发

如果解决这个高难度的问题呢? 答案就是使用redis 分段锁。

什么是分布式锁?

一个分布式系统中,存在客户端多个用户,同时通过多个业务微服务,发起一个数据修改。

如果没有分布式锁机制保证,在那多台机器上的多个服务可能进行并发修改操作,导致数据修改的不一致,出现脏读脏写,这就会造成问题。

而分布式锁机制就是为了解决类似这类问题,保证多个服务之间互斥的访问共享资源,如果一个服务抢占了分布式锁,其他服务没获取到锁,就不进行后续操作。

上图中,哪个客户端的服务抢占了分布式锁,就可以去扣减库存。

其他服务没获取到分布式锁,就不进行后续操作。

  • 当在分布式模型下,数据只有一份(或有限制),此时需要利用锁的技术控制某一时刻修改数据的进程数。

  • 用一个状态值表示锁,对锁的占用和释放通过状态值来标识。

分布式锁的条件:

  • 互斥性。在任意时刻,只有一个客户端能持有锁。

  • 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。

  • 具有容错性。只要大部分的 Redis 节点正常运行,客户端就可以加锁和解锁。

  • 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

普通的分布式锁如何实现?

分布式锁的实现由很多种,文件锁、数据库、redis等等,比较多;分布式锁常见的多种实现方式:

  1. 数据库悲观锁;

  2. 数据库乐观锁;

  3. 基于Redis的分布式锁;

  4. 基于ZooKeeper的分布式锁。

在实践中,还是redis做分布式锁性能会高一些

普通分布式锁的性能问题

分布式锁一旦加了之后,对同一个商品的下单请求,会导致所有下单操作,都必须对同一个商品key加分布式锁。

假设某个场景,一个商品1分钟6000订单,每秒的 600个下单操作,

假设加锁之后,释放锁之前,查库存 -> 创建订单 -> 扣减库存,每个IO操作100ms,大概300毫秒。

具体如下图:

可以再进行一下优化,将 创建订单 + 扣减库存 并发执行,将两个100ms 减少为一个100ms,这既是空间换时间的思想,大概200毫秒。

将 创建订单 + 扣减库存 批量执行,减少一次IO,也是大概200毫秒。也就是单个商品而言,只有 5 QPS.

假设一个商品sku的数量是10000,10秒内秒杀完,也就是单个商品而言,需要 单商品 100 QPS,如何应对一个商品的 100qps秒杀。

甚至,如果单商品需要 1000qps秒杀呢?

答案是,使用 分段加锁

第一次优化之后:使用Redis分段锁提升秒杀的并发性能

如何提高性能呢?空间换时间

为了达到每秒600个订单,可以将锁分成 600 /5 =120 个段,反过来, 每个段1秒可以操作5次, 120个段,合起来,及时每秒操作600次。

进行抢夺锁的,如果申请到一个具体的段呢?

  • 随机路由法

  • hash取模法

如果是用随机路由算法,可以将请求随机到一个分段, 如果不行,就轮询下一个分段,具体的流程,大致如下:

这个是一个理论的时间预估,没有扣除 尝试下一个分段的 时间, 另外,实际上的性能, 会比理论上差,从咱们实操案例的测试结果,也可以证明这点。

随机路由法的问题:

不同分端之间,可能库存消耗不均,导致部分用户无法扣减库存,反复进行重试,拖慢系统性能。

如何进一步优化:hash取模法。

第二次优化之后:使用hash取模法,减少库存消耗不均和无效重试

由于秒杀场景的分布式锁,实际上是为了防止超卖, 和库存是强相关的。

所以,可以结合库存,把秒杀的分布式锁进行改进。

第一步:把redis 的分段方式进行演进,额外增加一个总库存分段锁,用于分配存储剩余的总库存。采用多批次少量分配的思路,通过定时任务,从总库存向分段库存中迁移库存。

第二步:使用hash取模法,把用户路由到某一个分段,如果分段里边的库存耗光了,就去访问剩余的总库存。

库存动态迁移

为了防止分段多库存耗光,大家都去抢占总库存锁。

采用多批次少量分配的思路,通过定时任务,从总库存向分段库存中迁移库存。

至此, hash取模法的分段锁设计方案,已经完美实现。

相关推荐
容器( ु⁎ᴗ_ᴗ⁎)ु.。oO13 分钟前
MySQL事务
数据库·mysql
cyt涛2 小时前
MyBatis 学习总结
数据库·sql·学习·mysql·mybatis·jdbc·lombok
Rookie也要加油2 小时前
01_SQLite
数据库·sqlite
liuxin334455663 小时前
教育技术革新:SpringBoot在线教育系统开发
数据库·spring boot·后端
看山还是山,看水还是。3 小时前
MySQL 管理
数据库·笔记·mysql·adb
fishmemory7sec3 小时前
Koa2项目实战2(路由管理、项目结构优化)
数据库·mongodb·koa
momo小菜pa4 小时前
【MySQL 09】表的内外连接
数据库·mysql
Jasonakeke4 小时前
【重学 MySQL】四十九、阿里 MySQL 命名规范及 MySQL8 DDL 的原子化
数据库·mysql
程序猿小D4 小时前
第二百六十九节 JPA教程 - JPA查询OrderBy两个属性示例
java·开发语言·数据库·windows·jpa
小宇成长录4 小时前
Mysql:数据库和表增删查改基本语句
数据库·mysql·数据库备份