Redis 实现分布式锁的几种方案
GreatSQL 社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。
GreatSQL 是 MySQL 的国产分支版本,使用上与 MySQL 一致。
文章来源:GreatSQL 社区原创
1.前言
对于 Redis 实现分布式锁的几种方案这个话题,展开之前我想先简单聊聊什么是分布式锁,分布式锁的使用场景,除了 Redis 外还有什么技术实现分布式锁等一系列内容。
1.1 分布式锁
说大一点,就是在现在发展越来越迅速的大背景下,去中心化分布式系统越来越普及,在我们实际的生产开发当中,有一种不可避免的场景就是多个进程互斥的对其资源的使用,为了保证数据不重复,要求在同一时刻,同一任务只在一个节点上运行,且保证在多进程下的数据安全,分布式锁就十分重要了。
1.2 分布式锁的几种方案
方式有很多种,根据技术角度的不同
有基于 MySQL 的方式,通过表的唯一索引,通过 insert 和 delete 就可以实现加锁和解锁的效果;
有基于 zookeeper 的方式,通过创建临时有序节点,判断创建的节点序号是否最小。若是,则表示获取到锁,不是,则 watch /lock 目录下序号比自身小的前一个节点,解锁只需要删除节点;
有基于 Redis 的方式。通过执行 setnx,若成功再执行 expire 添加过期时间的方式加锁,解锁执行 delete 命令。
方式有很多,不一一列举了。
1.3Redis 分布式锁需要满足的条件
互斥性。在任意时刻,只有一个客户端能持有锁。
不发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁也能保证后续其他客户端能加锁。
同一性。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了,即不能误解锁。
容错性。只要大多数 Redis 节点正常运行,客户端就能够获取和释放锁。
2.Redis 实现分布式锁的几种方案
可以通过以下方式实现(包括但不限于):
SETNX + EXPIRE
SETNX + value(系统时间+过期时间)
通过开源框架-Redisson
简单来说说,用 Java 代码演示:
2.1 SETNX + EXPIRE
setnx
(SET IF NOT EXISTS)+ expire
命令。先用setnx
来抢锁,如果抢到锁,再用expire
给锁设置一个过期时间,这样持有锁超时时释放锁,防止锁忘记释放。但此时setnx
和expire
两个命令无法保证原子性,例如:
2.2 SETNX + value(系统时间+过期时间)
可以把过期时间放到setnx
的 value 值里面。如果加锁失败,再拿出 value 值校验一下即可。加锁代码如下:
2.3 通过开源框架-Redisson
那么此时就要去想了,如果已经超过了加锁的过期时间,可是业务还没执行完成,这个时候怎么做呢?是把过期时间延长吗?显然不合理,可以通过开源框架-Redisson 优化这个问题,简单来说,Redisson 就是当一个线程获得锁以后,给该线程开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放。假设两个线程争夺统一公共资源:线程 A 获取锁,并通过哈希算法选择节点,执行 Lua 脚本加锁,同时其看门狗机制会启动一个watch dog
(后台线程),每隔 10 秒检查线程,如果线程 A 还持有锁,那么就会不断的延长锁 key 的生存时间。线程 B 获得锁失败,就会订阅解锁消息,当获取锁到剩余过期时间后,调用信号量方法阻塞住,直到被唤醒或等待超时。一旦线程 A 释放了锁,就会广播解锁消息。于是,解锁消息的监听器会释放信号量,获取锁被阻塞的线程 B 就会被唤醒,并重新尝试获取锁。
Redisson 支持单点模式、主从模式、哨兵模式、集群模式,假设现为单点模式:
3.小结
Redis 的分布式锁实现方式有很多,这里不一一列举了,有机会再展开 Lua 脚本、分布式锁 Redlock 等内容。
Enjoy GreatSQL :)
关于 GreatSQL
GreatSQL 是由万里数据库维护的 MySQL 分支,专注于提升 MGR 可靠性及性能,支持 InnoDB 并行查询特性,是适用于金融级应用的 MySQL 分支版本。
相关链接: GreatSQL社区 Gitee GitHub Bilibili
GreatSQL 社区:
社区博客有奖征稿详情:https://greatsql.cn/thread-100-1-1.html
技术交流群:
微信:扫码添加
GreatSQL社区助手
微信好友,发送验证信息加群
。
版权声明: 本文为 InfoQ 作者【GreatSQL】的原创文章。
原文链接:【http://xie.infoq.cn/article/ad88fc10a1f7942ce11b5d3a0】。未经作者许可,禁止转载。
评论