最近和几个小伙伴聊了聊基于Redis的分布式锁实现秒杀扣减库存业务的一些技术细节,刚好最近钻研了一段时间,本篇内容通过1个详细的案例,把这个实现方案作个记录,当做自己对知识的总结积累,同时也欢迎广大开发者朋友一起交流,学习,大家可以留言讨论,原创写作不易,请勿喷,如果觉得有用,不要忘了关注点赞哦。
本案例我们通过以下6个部分来讲解基于Redis+Lua实现分布式锁的详细过程,案例背景是模拟秒杀扣减库存的经典业务:
1、什么是分布式锁 ?
要说起分布式锁,首先要提到与分布式锁相对应的是线程锁、进程锁。
线程锁:主要用来给方法、代码块加锁。当某个方法或代码块使用锁,在同一时刻仅有一个线程执行该方法或该代码段。线程锁只在同一JVM中有效果,因为线程锁的实现在根本上是不同线程之间对于同一JVM共享其内存实现的,比如synchronized是共享对象头,显示锁Lock是共享某个变量(state)。
进程锁:为了控制同一操作系统中多个进程访问某个共享资源,因为进程具有独立性,各个进程无法访问其他进程的资源,因此无法通过synchronized等线程锁实现进程锁。
分布式锁:当多个进程(多个应用程序)不在同一个系统中,比如微服务部署多节点,就要用分布式锁控制多个进程对资源的访问。
2、什么场景下需要分布式锁 ?
随着互联网的高速发展,一些电商系统承载的业务体量也在大大增加,那么企业为了应对巨大的业务体量,很多时候会用到微服务,单个服务会部署多个节点,以此来处理瞬间激增的用户请求,比如秒杀促销的时候,某个界面一下子有10000人同时抢购,那么这10000个人的请求会按照特定的负载均衡策略,把10000个用户请求路由到不同的多个节点去处理,大大减轻了单台应用处理业务的能力,提升了效率,提升了用户的体验度。那么这个时候就要用到分布式锁去满足技术方案。
3、用了分布式锁会带来什么好处 ?
接着上面的举例,如果没用到分布式锁,那么很可能出出现,用户1下单的代码过程中,还没等用户1处理完订单数据,用户2的请求进入到用户1下单的线程中了,这2个线程在同1个应用中执行,是共享的同1个JVM内存,那么可能会出现订单数据错乱,最终导致订单业务完全发生重大严重错误。但是如果采用了分布式锁,并且使用得当的情况下完全可以避免这个问题。这时就凸显分布式锁的重要作用了。
4、实现分布式锁可以采用哪些方案实现 ?
目前行业中分布式锁实现众所周知的主要有3种方案:
1.采用数据库的事务锁
2.采用Zookeeper框架
3.基于redis实现
目前主流的比较成熟并且比较大众化的方案就是基于redis实现,当然再组合上Lua,实现分布式锁实现过程会更简单,功能更强大。本案例笔者就以springboot+Jedis+redis+Lua通过具体的例子详细讲解分布式锁的实现过程和思路。
5、分布式锁需要满足的4个的必要条件
- 互斥性。在任意时刻,只有一个客户端线程能持有锁。
- 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也要保证后续其他客户端能加锁。
- 具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
- 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
6、代码实现
(1)、引入依赖
首先我们要通过Maven引入Jedis开源组件,在pom.xml文件加入下面的代码:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
(2)、正确加锁方式
/**
* @author guobh
* @date 2020年8月27日 上午11:10:50
* @version 1.0
* @since jdk1.8
* @description <>
*/
@Slf4j
@Component
public class RedisPool {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
private static final Long RELEASE_SUCCESS = 1L;
// 锁的过期时间
private static int EXPIRE_TIME = 500;
private static JedisPool pool;//jedis连接池对象
private stati

最低0.47元/天 解锁文章
550





