[转帖]【Redis学习06】分布式锁及其优化

redis,学习,分布式,及其,优化 · 浏览次数 : 0

小编点评

**分布式锁的实现** * 使用 stringRedisTemplate 的 `execute` 方法执行 Lua脚本。 * 经过 3 个版本的分布式锁迭代,确保锁获取成功。 * 在锁获取过程中,使用 Lua脚本执行原子操作。 * 在脚本中使用 `stringRedisTemplate` 的 `execute` 方法进行分布式锁操作。 **代码示例** ```python # 代码中省略了部分逻辑,请根据实际情况进行修改 stringRedisTemplate = get_redis_template() with stringRedisTemplate.acquire_lock(): # 执行 Lua脚本 result = stringRedisTemplate.execute( "some_lua_script", args=(argument1, argument2), ) # 检查结果 print(result) ``` **知识点** * 使用 stringRedisTemplate 的 `acquire_lock` 方法获取分布式锁。 * `acquire_lock` 方法接受一个 `LockOptions` 对象作为参数,其中包含锁策略等信息。 * `acquire_lock` 方法会阻塞,直到获取锁成功。 * `release_lock` 方法可以释放锁。 * `execute` 方法执行 Lua脚本并返回结果。 * Lua脚本可以使用 `stringRedisTemplate` 的方法进行分布式锁操作。

正文

前言

上一篇博客我们讲到秒杀问题的一人一单在单机模式下使用synchronized添加悲观锁能解决并发问题。

但是在集群模式下,我们使用悲观锁就无法解决并发问题,因为集群中每个java虚拟机不是共用一个锁,而是每个java虚拟机都拥有属于自己的锁,因而就无法保证在并发模式下的一人一单问题。
在这里插入图片描述

怎么解决呢?

那就要引入我们今天的要学习的内容—分布式锁

我们这里先简要介绍一下分布式锁原理:集群模式悲观锁失效是因为每个JVM有其独自的锁,而集群模式就必须共享同一个锁,因此,我们取消每个JVM中的锁,让所有的JVM都去共用同一个锁监视器,这就是分布式锁的基本思想。
在这里插入图片描述

1. 什么是分布式锁

分布式锁:满足分布式系统或者集群模式下多进程可见并且互斥的锁

它需要满足以下特点:
在这里插入图片描述

实现效果:同一时刻只有一个线程能拥有互斥锁,只有该线程释放互斥锁,其他线程才能获得互斥锁的使用权
在这里插入图片描述

2. 分布式锁的实现

在这里插入图片描述
这里我们使用redis提供的分布式锁功能。

2.1 基于Redis的分布式锁实现方法

在这里插入图片描述
这里我们要明确一个点,因为我们学习redis需要考虑在高并发模式下,很多线程共享资源的问题。资源共享就会涉及进程阻塞问题,因此每一步操作就需要尽量满足原子性,减少进程阻塞导致的并发问题。

因此,我们在获取锁的方式可以修改为如下方式

将设置互斥锁和超时时间同步完成,是为了解决在设置锁的时候发生进程阻塞导致锁未设置超时时间,导致锁无法释放的问题
在这里插入图片描述
OK,接下来我们就根据以上方法设置我们的第一个分布式锁。

2.2 基于redis实现分布式锁的初级版本

  1. 需求分析
    在这里插入图片描述

  2. 梳理流程
    在这里插入图片描述

  3. 代码实现

public class SimpleRedisLock implements ILock{
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">String</span> KEY_PREFIX <span class="token operator">=</span> <span class="token string">"lock:"</span><span class="token punctuation">;</span>
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">String</span> ID_PREFIX <span class="token operator">=</span> UUID<span class="token punctuation">.</span><span class="token function">randomUUID</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token operator">+</span><span class="token string">"-"</span><span class="token punctuation">;</span>

<span class="token keyword">private</span> <span class="token class-name">StringRedisTemplate</span> stringRedisTemplate<span class="token punctuation">;</span>
<span class="token keyword">private</span> <span class="token class-name">String</span> name<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token class-name">SimpleRedisLock</span><span class="token punctuation">(</span><span class="token class-name">StringRedisTemplate</span> stringRedisTemplate<span class="token punctuation">,</span> <span class="token class-name">String</span> name<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>stringRedisTemplate <span class="token operator">=</span> stringRedisTemplate<span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token annotation punctuation">@Override</span>
<span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">tryLock</span><span class="token punctuation">(</span><span class="token keyword">long</span> timeSec<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    <span class="token comment">//获取线程id</span>
    <span class="token class-name">String</span> threadId <span class="token operator">=</span> ID_PREFIX<span class="token operator">+</span><span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">//获取锁</span>
    <span class="token class-name">Boolean</span> success <span class="token operator">=</span> stringRedisTemplate<span class="token punctuation">.</span><span class="token function">opsForValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">setIfAbsent</span><span class="token punctuation">(</span>KEY_PREFIX <span class="token operator">+</span> name<span class="token punctuation">,</span> threadId<span class="token punctuation">,</span> timeSec<span class="token punctuation">,</span> <span class="token class-name">TimeUnit</span><span class="token punctuation">.</span>SECONDS<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">//返回的是boolean类型,是基本数据类型,而我们返回的Boolean类型是包装类,避免拆箱出现空指针。</span>
    <span class="token keyword">return</span> <span class="token class-name">Boolean</span><span class="token punctuation">.</span>TRUE<span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>success<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token annotation punctuation">@Override</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">unLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    <span class="token comment">//删除锁</span>
    stringRedisTemplate<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span>KEY_PREFIX <span class="token operator">+</span> name<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

这里说明两点

  • 我们设置的锁需要满足互斥以及具有超时时间,我们使用setIfAbsent()方法,它不仅满足互斥条件,还能同时设置超时时间,满足需要。
    在这里插入图片描述

  • 拆箱与装箱

1)什么是拆箱
• 拆箱就是自动将包装器类型转换为基本数据类型
• 拆箱调用Integer.intValue方法

2)什么是装箱
• 装箱就是自动将基本数据类型转换为包装器类型
• 装箱调用的Integer.valueOf方法
在这里插入图片描述

我们在尝试获取锁的方法中返回值时基本数据类型,而设置锁返回的时包装器类型,如果我们将其直接返回会涉及拆箱和装箱过程,我们在开发过程中要尽量避免拆箱和装箱,因为可能会出现空指针问题
在这里插入图片描述

在秒杀券的一人一单代码实现之前获取我们设置的分布式互斥锁,添加基本的分布式锁,由于后面执行代码可能抛异常,因此需要try/finally释放锁

 //添加基本的分布式锁,由于后面执行代码可能抛异常,因此需要try/finally释放锁
        SimpleRedisLock redisLock = new SimpleRedisLock(redisTemplate, "order:" + userId);
        boolean flag = redisLock.tryLock(5);
        if(!flag){
            return Result.fail("一人只能购买一张优惠券");
        }
    <span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span>
        <span class="token comment">//一人一单</span>
        <span class="token keyword">int</span> count <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">query</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">eq</span><span class="token punctuation">(</span><span class="token string">"user_id"</span><span class="token punctuation">,</span> userId<span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">eq</span><span class="token punctuation">(</span><span class="token string">"voucher_id"</span><span class="token punctuation">,</span> voucherId<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">count</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span>count<span class="token operator">&gt;</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">{<!-- --></span>
            <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">fail</span><span class="token punctuation">(</span><span class="token string">"一个用户只能购买一个优惠券"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token comment">//当更新时查询的库存大于0时进行库存减一</span>
        <span class="token keyword">boolean</span> success <span class="token operator">=</span> seckillVoucherService<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">setSql</span><span class="token punctuation">(</span><span class="token string">"stock=stock-1"</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">gt</span><span class="token punctuation">(</span><span class="token string">"voucher_id"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">eq</span><span class="token punctuation">(</span><span class="token string">"stock"</span><span class="token punctuation">,</span> voucher<span class="token punctuation">.</span><span class="token function">getStock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>success<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
            <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">fail</span><span class="token punctuation">(</span><span class="token string">"优惠券已被抢完"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token comment">//6. 创建订单</span>
        <span class="token comment">//6.1 设置id</span>
        <span class="token class-name">VoucherOrder</span> voucherOrder <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">VoucherOrder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">Long</span> voucherOrderId <span class="token operator">=</span> <span class="token class-name">RedisIdWorker</span><span class="token punctuation">.</span><span class="token function">nextId</span><span class="token punctuation">(</span><span class="token string">"voucherOrder"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        voucherOrder<span class="token punctuation">.</span><span class="token function">setId</span><span class="token punctuation">(</span>voucherOrderId<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token comment">//6.2 设置user_id</span>
        voucherOrder<span class="token punctuation">.</span><span class="token function">setUserId</span><span class="token punctuation">(</span>userId<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token comment">//6.3 设置优惠券id</span>
        voucherOrder<span class="token punctuation">.</span><span class="token function">setVoucherId</span><span class="token punctuation">(</span>voucherId<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>voucherOrder<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token class-name">Result</span><span class="token punctuation">.</span><span class="token function">ok</span><span class="token punctuation">(</span>voucherOrderId<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{<!-- --></span>
        redisLock<span class="token punctuation">.</span><span class="token function">unLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

2.3 改进分布式锁

我们结合下面的图进行分析一下使用上述分布式锁可能发生的情况:

  1. 当线程1获取redis锁后执行业务,这时业务阻塞,因而导致redis锁超时释放。
  2. 线程2此时获取到redis锁,执行业务,线程2执行业务过程中,线程1被唤醒,完成业务释放了redis锁,而业务2还在执行。
  3. 线程3获取redis锁,也开始执行业务,此时,线程2和线程3都在执行业务,我们的redis锁在并发模式下又形同虚设了。

在这里插入图片描述
怎么解决呢?

我们想想之前是如何解决超卖问题呢?是不是使用了CAS方法。即扣减库存的时候判断库存是否跟之前查询的库存相等,相等再去扣减库存。

方法类似,即我们在删除锁的时候可以设置一个线程标识,标识此时是哪个线程获取了锁。假如一旦获取锁的线程1阻塞,锁超时释放,此时线程2获取了锁,将锁的标识更换了,线程2执行业务的时候线程1唤醒,想要去释放锁,结果发现锁已经不是自己的,就不去释放。问题就解决了。

ok,我们看一下解决的流程图
在这里插入图片描述
我们只需要在删除锁的代码进行修改即可。

	public void unLock() {
    <span class="token comment">//获取当前线程ID</span>
    <span class="token class-name">String</span> currentId <span class="token operator">=</span> stringRedisTemplate<span class="token punctuation">.</span><span class="token function">opsForValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>KEY_PREFIX <span class="token operator">+</span> name<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">//线程标识</span>
    <span class="token class-name">String</span> threadId <span class="token operator">=</span> ID_PREFIX<span class="token operator">+</span><span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">//判断当前线程与线程标识是否相同,相同再进行释放</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>threadId<span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>currentId<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        <span class="token comment">//删除锁</span>
        stringRedisTemplate<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span>KEY_PREFIX <span class="token operator">+</span> name<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">//删除锁</span>
    <span class="token comment">//stringRedisTemplate.delete(KEY_PREFIX + name);</span>

<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

2.4 基于Lua脚本改善分布式锁

上述改进能否完美解决因线程阻塞而导致的锁错误释放问题呢?

我们不妨来看一下下面这张图。

看不明白没关系,我帮你们读懂一下。

  1. 首先,线程1尝试获取锁成功,执行业务,执行完成后,尝试去释放锁,当程序判断完成是自己的锁后刚要去执行释放锁操作,诶,真不巧 ,线程被阻塞了,锁又被超时释放了。
  2. 这时,线程2尝试获取锁成功,执行业务,此时,线程1又醒了,醒了还没完,因为线程1已经判断锁是自己的,因此人家醒了二话不说就把锁给释放了
  3. 最后,线程3进来了,它也获取锁成功,执行业务,此时线程2和线程3就并行执行业务,我们的梦想又破灭了。。。
    在这里插入图片描述

怎么解决呢?

我们想想问题的关键是不是因为判断锁和删除锁是分开进行的,因而才会出现线程阻塞问题。那我们能不能将这两步操作改成原子级别的操作呢?

单纯靠java实现可能不太行,这里就要引入我们要讲的主角,Lua脚本

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

ok,现在我们基于Lua脚本来改进一下我们的释放锁的逻辑。

Lua脚本

-- 比较线程标示与锁中的标示是否一致
if(redis.call('get', KEYS[1]) ==  ARGV[1]) then
    -- 释放锁 del key
    return redis.call('del', KEYS[1])
end
return 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
public class SimpleRedisLock implements ILock{
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">String</span> KEY_PREFIX <span class="token operator">=</span> <span class="token string">"lock:"</span><span class="token punctuation">;</span>
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">String</span> ID_PREFIX <span class="token operator">=</span> UUID<span class="token punctuation">.</span><span class="token function">randomUUID</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token operator">+</span><span class="token string">"-"</span><span class="token punctuation">;</span>

<span class="token comment">//代码开始就加载静态代码块,也就是lua脚本</span>
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">DefaultRedisScript</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Long</span><span class="token punctuation">&gt;</span></span> UNLOCK_SCRIPT<span class="token punctuation">;</span>
<span class="token keyword">static</span> <span class="token punctuation">{<!-- --></span>
    UNLOCK_SCRIPT <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DefaultRedisScript</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    UNLOCK_SCRIPT<span class="token punctuation">.</span><span class="token function">setLocation</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ClassPathResource</span><span class="token punctuation">(</span><span class="token string">"unlock.lua"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    UNLOCK_SCRIPT<span class="token punctuation">.</span><span class="token function">setResultType</span><span class="token punctuation">(</span><span class="token class-name">Long</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">private</span> <span class="token class-name">StringRedisTemplate</span> stringRedisTemplate<span class="token punctuation">;</span>
<span class="token keyword">private</span> <span class="token class-name">String</span> name<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token class-name">SimpleRedisLock</span><span class="token punctuation">(</span><span class="token class-name">StringRedisTemplate</span> stringRedisTemplate<span class="token punctuation">,</span> <span class="token class-name">String</span> name<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>stringRedisTemplate <span class="token operator">=</span> stringRedisTemplate<span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token annotation punctuation">@Override</span>
<span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">tryLock</span><span class="token punctuation">(</span><span class="token keyword">long</span> timeSec<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    <span class="token comment">//获取线程id</span>
    <span class="token class-name">String</span> threadId <span class="token operator">=</span> ID_PREFIX<span class="token operator">+</span><span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">//获取锁</span>
    <span class="token class-name">Boolean</span> success <span class="token operator">=</span> stringRedisTemplate<span class="token punctuation">.</span><span class="token function">opsForValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">setIfAbsent</span><span class="token punctuation">(</span>KEY_PREFIX <span class="token operator">+</span> name<span class="token punctuation">,</span> threadId<span class="token punctuation">,</span> timeSec<span class="token punctuation">,</span> <span class="token class-name">TimeUnit</span><span class="token punctuation">.</span>SECONDS<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">//返回的是boolean类型,是基本数据类型,而我们返回的Boolean类型是包装类,避免拆箱出现空指针。</span>
    <span class="token comment">/*boolean flag = Boolean.TRUE.equals(success);
    return flag;*/</span>
    <span class="token keyword">return</span> <span class="token class-name">Boolean</span><span class="token punctuation">.</span>TRUE<span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>success<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token annotation punctuation">@Override</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">unLock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>

    <span class="token comment">//使用lua脚本执行原子级别的操作,不会因为线程阻塞导致释放锁发生错误。</span>
    stringRedisTemplate<span class="token punctuation">.</span><span class="token function">execute</span><span class="token punctuation">(</span>UNLOCK_SCRIPT<span class="token punctuation">,</span>
            <span class="token class-name">Collections</span><span class="token punctuation">.</span><span class="token function">singletonList</span><span class="token punctuation">(</span>KEY_PREFIX <span class="token operator">+</span> name<span class="token punctuation">)</span><span class="token punctuation">,</span>
            ID_PREFIX<span class="token operator">+</span><span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

执行lua脚本调用 stringRedisTemplate.execute的API,传递三个参数
在这里插入图片描述
在这里插入图片描述
经过三个版本的分布式锁迭代,我们设计的锁就基本能够满足我们的使用需求了。

文章知识点与官方知识档案匹配,可进一步学习相关知识

与[转帖]【Redis学习06】分布式锁及其优化相似的内容:

[转帖]【Redis学习06】分布式锁及其优化

文章目录 前言1. 什么是分布式锁2. 分布式锁的实现2.1 基于Redis的分布式锁实现方法2.2 基于redis实现分布式锁的初级版本2.3 改进分布式锁2.4 基于Lua脚本改善分布式锁 前言 上一篇博客我们讲到秒杀问题的一人一单在单机模式下使用synchronized添加悲观锁能解决并发问题

[转帖]【Redis学习06】分布式锁及其优化

文章目录 前言1. 什么是分布式锁2. 分布式锁的实现2.1 基于Redis的分布式锁实现方法2.2 基于redis实现分布式锁的初级版本2.3 改进分布式锁2.4 基于Lua脚本改善分布式锁 前言 上一篇博客我们讲到秒杀问题的一人一单在单机模式下使用synchronized添加悲观锁能解决并发问题

[转帖]【Redis学习06】分布式锁及其优化

文章目录 前言1. 什么是分布式锁2. 分布式锁的实现2.1 基于Redis的分布式锁实现方法2.2 基于redis实现分布式锁的初级版本2.3 改进分布式锁2.4 基于Lua脚本改善分布式锁 前言 上一篇博客我们讲到秒杀问题的一人一单在单机模式下使用synchronized添加悲观锁能解决并发问题

[转帖]Redis学习笔记--Redis数据过期策略详解

本文对Redis的过期机制简单的讲解一下 讲解之前我们先抛出一个问题,我们知道很多时候服务器经常会用到redis作为缓存,有很多数据都是临时缓存一下,可能用过之后很久都不会再用到了(比如暂存session,又或者只存放日行情股票数据)那么就会出现一下几个问题了 Redis会自己回收清理不用的数据吗?

[转帖]Redis学习六(Redis 阻塞的原因及其排查方向).

https://www.cnblogs.com/jmcui/p/13926397.html 一、慢查询 因为 Redis 是单线程的,大量的慢查询可能会导致 redis-server 阻塞,可以通过 slowlog get n 获取慢日志,查看详情情况。 回到顶部 二、bigkey 大对象 bigk

[转帖]Redis学习五(Spring Cache For Redis).

https://www.cnblogs.com/jmcui/p/8410560.html 一、概述 缓存(Caching)可以存储经常会用到的信息,这样每次需要的时候,这些信息都是立即可用的。 常用的缓存数据库: Redis 使用内存存储(in-memory)的非关系数据库,字符串、列表、集合、散列

[转帖]Redis学习二(数据操作).

阅读目录 key 操作 string 操作 list 操作 set 操作 zset 操作 hash 操作 HyperLogLog 操作 回到顶部 key 操作 删除 key:del key 批量删除key:redis-cli -a(密码)keys “QXJ_*”| xargs redis-cli -

[转帖]Redis学习三(进阶功能).

https://www.cnblogs.com/jmcui/p/11707970.html 阅读目录 一、排序 二、事务 三、流水线(pipeline) 四、发布订阅 回到顶部 一、排序 redis 支持对 list,set 和 zset 元素的排序,排序的时间复杂度是 O(N+M*log(M))。

[转帖]Redis学习四(运维指南).

阅读目录 一、上线规划 二、常见运维操作 三、测试方法 回到顶部 一、上线规划 一般 redis 的参数配置都在 redis.conf 中,在上线前根据实际环境配置好合适参数,能有效提高 redis 的可用性。 redis 的运行机器 CPU 不求核数多,但求主频高,Cache大,因为 redis

[转帖]Redis进阶实践之四Redis的基本数据类型

一、引言 今天正式开始了Redis的学习,如果要想学好Redis,必须先学好Redis的数据类型。Redis为什么会比以前的Memchaed等内存缓存软件使用的更频繁,适用范围更广呢?就是因为Redis使用起来更方便,之所以方便,是因为Redis支持的数据类型比以前的Memchaed缓存支持数据类型