【Demo不推荐】使用Redis的setnx指令实现 分布式锁 解决一人一单问题

1 原理:setnx指令

setnx指令的特点:setnx只能设置key不存在的值,值不存在设置成功,返回 1 ;值存在设置失败,返回 0

文档地址 https://redis.io/docs/latest/commands/setnx/

如果键不存在,则将键设置为保存字符串值。在这种情况下,它相当于 SET。如果键已保存值,则不执行任何操作。SETNX 是“SET if Not eXists”的缩写。
Set key to hold string value if key does not exist. In that case, it is equal to SET. When key already holds a value, no operation is performed. SETNX is short for "SET if Not eXists".

获取锁:

方式一:

# 添加锁
setnx [key] [value]
# 为锁设置过期时间,超时释放,避免死锁
expire [key] [time]

方式二:这种方式更加推荐,因为将上面两个指令变成一个指令,从而保障指令的原子性

# 添加锁
set [key] [value] ex [time] nx

释放锁:

# 释放锁(除了使用del手动释放,还可超时释放)
del [key]

2 实现过程

2.1 简陋版1(有问题,释放了别人的锁)

居中图片

有问题:

  • 当线程1获取锁后,由于业务阻塞,线程1的锁超时释放了,
  • 这时候线程2趁虚而入拿到了锁,然后此时线程1业务完成了,然后把线程2刚刚获取的锁给释放了,
  • 这时候线程3又趁虚而入拿到了锁,这就导致又出现了超卖问题!(但是这个在小项目(并发数不高)中出现的概率比较低,在大型项目(并发数高)情况下是有一定概率的)

2.2 简陋版2(有问题, 判断锁和释放锁可能被打断,为保证原子性)

为分布式锁添加一个线程标识,

  • 在释放锁时判断当前锁是否是自己的锁,是自己的就直接释放,
  • 不是自己的就不释放锁,从而解决多个线程同时获得锁的情况导致出现超卖
    居中图片

实现思路:

  1. 加锁时
    • Redis中key为锁名
    • uuid+Thread的id作为redis中的value
  2. 释放锁时
    • uuid+Thread的id和Redis中的值比较,一致再释放锁。

仍然有问题:

  • 线程1获取锁,执行完业务然后并且判断完当前锁是自己的锁时,但就在此时发生了阻塞,结果锁被超时释放了,
  • 线程2立马就趁虚而入了,获得锁执行业务,
  • 但就在此时线程1阻塞完成,由于已经判断过锁,已经确定锁是自己的锁了,于是直接就删除了锁,结果删的是线程2的锁,
  • 这就又导致线程3趁虚而入了,从而继续发生超卖问题

产生原因:
判断锁和释放锁在同一个方法中,并且两者之间没有别的代码,为什么会发生阻塞呢?

  • JVM的垃圾回收机制会导致短暂的阻塞(csdn作者个人感觉这种情况发生的概率真的不高)

2.3 简陋版3

  1. 该如何保障 判断锁 和 释放锁 这连段代码的原子性呢?

    • 使用Lua脚本
  2. 那么Lua脚本是如何确保原子性的呢?

    • Redis使用(支持)相同的Lua解释器,来运行所有的命令。
    • Redis还保证脚本以原子方式执行:在执行脚本时,不会执行其他脚本或Redis命令。这个语义类似于MULTI(开启事务)/EXEC(触发事务,一并执行事务中的所有命令)。从所有其他客户端的角度来看,脚本的效果要么仍然不可见,要么已经完成。

注意:虽然Redis在单个Lua脚本的执行期间会暂停其他脚本和Redis命令,以确保脚本的执行是原子的,但如果Lua脚本本身出错,那么无法完全保证原子性。也就是说Lua脚本中的Redis指令出错,会发生回滚以确保原子性,但Lua脚本本身出错就无法保障原子性

  1. 实现
    释放锁的业务流程是这样的:
  • 获取锁中的线程标示
  • 判断是否与指定的标示(当前线程标示)一致
    • 如果一致则释放锁(删除)
    • 如果不一致则什么都不做

此时满足了

  • 多线程可见,将锁放到Redis中,所有的JVM都可以同时看到
  • 互斥,set ex nx指令互斥
  • 高可用,层层优化,即使是特别极端的情况下照样可以防止超卖
  • 高性能,Redis的IO速度很快,Lua脚本的性能也很快
  • 安全性,这个不用多说了,通过给锁夹线程标识+Lua封装Redis指令充分保障了线程安全,不那么容易出现并发安全问题,同时采用超时释放避免死锁
posted @ 2025-04-13 12:58  kuki'  阅读(73)  评论(0)    收藏  举报