Redis实现分布式锁用图说话
上一篇博客简单了介绍redisTemplate文章地址的基本使用,本篇博客介绍如何使用redis实现分布式锁。
一、进程级别单机锁
在传统的单机应用中,java内部提供的锁十分强大能够很好的解决并发问题,如synchronized
,ReentrantLock
,及juc
包下的类。synchronized
是进程级别的锁,也就是说只能在一个jvm进程中有效的控制多线程的并发问题。如下图:
二、分布式锁
随着现在架构的升级,单点已经不能满足我们的需要。我们的web应用通常是部署多个,然后通过nginx负载均衡。示意图如下:
在这种多点部署点情况下,如果采用单应用的锁如下图:
就无法保证数据的一致性了。当减库存的时候虽然每个jvm都加上锁的控制,但是数据还是会出现不一致。如当减库存时候,还是剩最后一件商品。有三个用户在同一个时刻发起请求。正好nginx把每一个请求单独发送到一个web应用上,然后他们都发现还有一件商品,然后能够进行购买,进行库存减1的操作。那么就会出现超卖的现象。此时就需要一个每个web应用都能访问的地方来请求锁。有一个全局的公共区域来管理着锁,这是强大的redis就该上场了
当我们首次请求的时候都向存放一个k-v
,然后还有请求过来我们会再次尝试加k-v
,如果已经存在,那么就相当于加锁失败。当执行完成后会释放锁,也就把redis里的这个 k-v
删除。如下图:
注:加锁 = 存一个k-v;释放锁 = 删除k-v
三、如何使用redis实现分布式锁
3.1 关于redis
redis适合做这个分布式锁有一个很大的原因,他是天然单线程的。当有指令过去时会一排着一个阻塞执行。
了解了整体的实现原理,那么一把简单的分布式锁就容易实现了,下面使用RedisTemplate来对redis进行操作。代码如下:
3.2加锁
/**
* 加锁, 加锁成功返回true, 失败返回false
*
* @param redisTemplate
* @param key
* @return
* @Author liuzihao
*/
public static boolean tryLock(RedisTemplate redisTemplate, String key) {
boolean result = false;
String status = (String) redisTemplate.execute((RedisCallback<String>) redisConnection -> {
Jedis jedis = (Jedis) redisConnection.getNativeConnection();
// 相当于setnx方法,不存在才设置,
// key为传入的key
String res = jedis.set(key, "1", "nx");
return res;
});
if ("OK".equals(status)) {//抢到锁
result = true;
}
return result;
}
3.3 解锁
在解锁过程中我们需要确认是否有存在这把锁,然后存在就把它操作。
但是这其实是两个步骤:1⃣️判断是否存在 2⃣️:存在就删除
(是不是会有疑问,直接删除就可以了,为什么还要判断是否存在,因为有时候我们会需要根据释放锁的结果进行业务操作)。我们需要步骤1⃣️和步骤2⃣️ 两个合二为一 以原子的方式执行,这个时候就可以使用lua
脚本来保证执行的原子性。可以将我们将两个步骤以一条指令发送给redis进行处理。
/**
* 释放锁, 通过lua脚本保证原子性
*
* @param redisTemplate
* @param key
* @return
* liuzihao
*/
public static void unLock(RedisTemplate redisTemplate, String key) {
// 这里其实就是构造一个不可变的list 使用了 谷歌的gava
ImmutableList<String> keys = ImmutableList.of(StringUtils.join(Collections.singleton(key), ""));
// lua脚本
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(script, String.class), keys, 1);
}
上面这种方式只是加锁解锁,并为考虑分布式redis主机宕机 ,然后重新选举后,导致锁不存在或失效的问题;或者单机redis模式下 突然宕机的异常情况。
四、分布式锁框架 redission
基于Redis的Java内存数据网格,最艺术的Redis Java客户端。能很好的解决分布式锁的问题
使用它实现分布式加锁解锁很简单,只要进行简单的配置后。调用对应的api即可。此处就不过多介绍
本文地址:https://blog.csdn.net/weixin_43732955/article/details/107513733