欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Redis实现分布式锁用图说话

程序员文章站 2022-03-26 18:06:04
上一篇博客简单了介绍redisTemplate文章地址的基本使用,本篇博客介绍如何使用redis实现分布式锁。目录一、进程级别单机锁二、分布式锁三、如何使用redis实现分布式锁四、分布式锁框架 redission一、进程级别单机锁在传统的单机应用中,java内部提供的锁十分强大能够很好的解决并发问题,如synchronized,ReentrantLock,及juc包下的类。synchronized是进程级别的锁,也就是说只能在一个jvm进程中有效的控制多线程的并发问题。如下图:二、分布式锁....

上一篇博客简单了介绍redisTemplate文章地址的基本使用,本篇博客介绍如何使用redis实现分布式锁。


一、进程级别单机锁

在传统的单机应用中,java内部提供的锁十分强大能够很好的解决并发问题,如synchronized,ReentrantLock,及juc包下的类。synchronized是进程级别的锁,也就是说只能在一个jvm进程中有效的控制多线程的并发问题。如下图:
Redis实现分布式锁用图说话

二、分布式锁

随着现在架构的升级,单点已经不能满足我们的需要。我们的web应用通常是部署多个,然后通过nginx负载均衡。示意图如下:
Redis实现分布式锁用图说话
在这种多点部署点情况下,如果采用单应用的锁如下图:
Redis实现分布式锁用图说话
就无法保证数据的一致性了。当减库存的时候虽然每个jvm都加上锁的控制,但是数据还是会出现不一致。如当减库存时候,还是剩最后一件商品。有三个用户在同一个时刻发起请求。正好nginx把每一个请求单独发送到一个web应用上,然后他们都发现还有一件商品,然后能够进行购买,进行库存减1的操作。那么就会出现超卖的现象。此时就需要一个每个web应用都能访问的地方来请求锁。有一个全局的公共区域来管理着锁,这是强大的redis就该上场了
当我们首次请求的时候都向存放一个k-v,然后还有请求过来我们会再次尝试加k-v,如果已经存在,那么就相当于加锁失败。当执行完成后会释放锁,也就把redis里的这个 k-v删除。如下图:
Redis实现分布式锁用图说话
注:加锁 = 存一个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客户端。能很好的解决分布式锁的问题

Redis实现分布式锁用图说话
使用它实现分布式加锁解锁很简单,只要进行简单的配置后。调用对应的api即可。此处就不过多介绍

本文地址:https://blog.csdn.net/weixin_43732955/article/details/107513733

相关标签: redis