分布式锁的概念

前言

在分布式系统中, 每个服务器都是一个单独的进程, 因为进程之间的执行顺序又是随机的, 很容易导致严重问题.

以买票系统为例, 如果没有分布式锁的话, 由于进程的随机性, 可能会导致一张票卖给两个人引发"超卖"问题. 所以引入分布式锁来解决:

例如当买票服务器1想进行买车次001的票时, 会先访问Redis, 寻找有没有其他服务器在买车次001的票, 如果没发现就在Redis上设置一个关于001的key. 此时如果其他服务器也想买001的票但是发现Redis上已经存在了这个key, 则不会再买票. 这样就达到了加锁的目的.

用Redis进行"加锁"的具体流程

利用Redis中的"set nx"

Redis中的"set nx"命令格式如下:

复制代码
SET key value NX

这样的效果是: 如果Redis上存在这样的Key就不会进行设置, 没有才会设置Key.

当以这样的方式进行"加锁"操作后, 我们同样需要对"锁"进行释放. 而利用del命令即可删除Key达到释放的效果.

为Key设定一个过期时间

如果一个服务器已经对Redis添加了Key, 但是这个服务器意外崩溃, 此时的"锁"还没有释放, 其他服务器只能进行等待. 为避免这一情况, 设置Key时需要添加一个"过期时间"的选项, 命令格式如下:

复制代码
SETNX key value NX EX (时间, 单位为秒)

避免这个服务器长时间占用"锁".

为服务器设置校验ID

有一种情况: 服务器1进行了set nx加锁了, 但服务器2执行了del释放了锁. 为避免这一情况, 需要对释放锁操作进行一个条件判定: 为所有服务器设置一个ID, 在释放锁时对服务器ID进行判定, 是否为加锁的服务器, 确定后再删除.

设置校验ID的流程如下(以伪代码形式说明):

引入Lua

根据上面设置服务器ID的过程中, 可以发现这个操作并非原子性的, 这就意味着可以被其他操作来"插队", 例如可能在验证的后另外一个服务器又进行了"set"操作, 当执行到del时就会释放另外一个服务器的锁了

为了解决这种问题, 采用Lua脚本的方式将这种非原子性的命令写入进去, 因为Redis在执行Lua脚本时是原子性的. 达到了事务的效果

写入Lua脚步执行的代码:

复制代码
if redis.call('get',KEYS[1]) == ARGV[1] then 
   return redis.call('del',KEYS[1]) 
else
   return 0 
end;

引入看门狗(watch dog)

在设置Key的过期时间时, 需要考虑到: 时间不能过短,防止服务器的操作未执行完就进行释放锁. 也不能设置时间过长,导致长时间占用锁,使其他服务器无法进行操作.

而引入看门狗机制可以有效解决问题: 在服务器中设置一个专门监视的线程, 如果快要到过期时间且服务器的操作还未结束, 那么将继续延长过期时间, 直到操作完成.

使用ReadLock算法

在使用Redis实现分布式锁时, 需要注意Redis节点的宕机, 这是致命性的. Redis也给出了对应办法

RedLock算法:

在Redis中使用"哨兵"机制, 实现主节点宕机,从节点晋升. 我们引入多 组 Redis 节点. 其中每⼀组 Redis 节点都包含⼀个主节点和若干从节点. 并且组和组之间存 储的数据都是⼀致的, 相互之间是 "备份" 关系(而并非是数据集合的⼀部分, 这点有别于 Redis cluster). 加锁的时候, 按照⼀定的顺序, 写多个 master 节点. 在写锁的时候需要设定操作的 "超时时间". 比如 50ms. 即如果 setnx 操作超过了 50ms 还没有成功, 就视为加锁失败.如果给某个节点加锁失败, 就⽴即再尝试下⼀个节点. 当加锁成功的节点数超过总节点数的⼀半, 视为加锁成功.

如上图, ⼀共五个节点, 三个加锁成功, 两个失败, 此时视为加锁成功. 这样的话, 即使有某些节点挂了, 也不影响锁的正确性. 而释放锁时同样需要对所有Redis服务器进行释放锁操作

相关推荐
城管不管11 小时前
重生——第九次面试2026.8.13某车一面
分布式·ai·面试·职场和发展·rabbitmq
ly768912 小时前
分布式一致性算法详解:从 2PC、3PC 到 Paxos、Raft、ZAB
分布式·算法
珠***格13 小时前
通信链路全打通:西格电力四可装置的 4G/5G + 加密认证技术详解
大数据·数据库·分布式·5g·架构·能源
ACP广源盛1392462567314 小时前
WAIC2026 国产算力浪潮@ACP#YLB3118 在算力矩阵中的存储定位与落地场景
大数据·人工智能·分布式·单片机·嵌入式硬件
蜀道山老天师17 小时前
ZABBIX分布式监控Proxy应用实践
分布式·zabbix
liudashuang201717 小时前
Kafka Producer 隐藏深坑:Sender 线程自阻塞(自死锁)导致 BufferExhaustedException 完整复盘
java·分布式·kafka·linq
rustfs19 小时前
GitLab 如何与 RustFS 集成?
分布式·rust·gitlab
Francek Chen19 小时前
【大数据处理与分析】数据仓库Hive:02 数据湖
大数据·数据仓库·hive·hadoop·分布式·数据分析
starzy199020 小时前
Spark 核心之二次排序、分组取 TopN 优化分析
大数据·分布式·spark
悦数图数据库20 小时前
千亿点边图谱建设,原生分布式图数据库如何避免单点瓶颈
数据库·分布式