如何通过 Redis 实现一个分布式锁

关于在golang中通过Redis实现分布式锁,很容易想到的是使用SET mylock 1 NX的形式,SET用于添加或更新一个键值对,mylock是键,作为锁的名称,1是值,此处可以是任意一个常量,NX是Not eXist,仅当key不存在时才执行SET。在释放锁时使用DEL mylock。mylock就是各进程争抢的一个临界资源。

但这么做显然有一些问题,接下来通过问题进行改进:

问题1 如何保证只有持有锁的进程才能释放锁?

如果A进程持有锁,B进程出现异常,错误的认为自己拥有了锁,然后将锁释放,可能会导致出现死锁或者数据不一致的状态。

解决方案是将mylock的值设置为随机数,只有知道随机数的进程才可以释放锁。

加锁:

redis 复制代码
SET mylock 7836 NX EX 30

释放锁时,检测锁的值和删除锁必须是原子操作,否则,在检测和删除锁之间插入其它操作会带来风险。释放锁需要用redis的lua脚本。

redis 复制代码
if redis.call("get","mylock") == "7836" then
    return redis.call("del","7836")
else
    return 0
end

问题2 持有锁的进程崩溃了怎么办?

如果持有锁的进程崩溃了,mylock将永远不会被删除,从而导致其他进程永远获取不到锁。

解决方案是为mylock设置过期时间,SET .. EX 30 表示mylock在30秒后key将因过期而失效。因此即使持有锁的进程崩溃了,其它进程也可以在最多30秒后获取到锁。

问题3 如何估计锁的过期时间?

如果设置的过期时间过短会导致锁被提前释放,带来风险,而设置的过期时间过长且进程意外退出时,会降低系统性能。

解决方法是启动一个后台线程,设置定时任务,当锁将要过期时进行续租:

redis 复制代码
SET mylock 7836 EX 4 // 为锁续租4秒

问题4 操作Redis失败了怎么办?

操作Redis失败的场景有三个:在获取锁时、在续租时、在释放锁。

在获取锁时重试,需要考虑的参数是重试间隔和最大重试次数,由锁来完成这个步骤,可以避免使用分布式锁的用户自己重试。

在续租时,为了避免网络问题导致续租失败,可以在过期前提前一段时间开始续租。但是如果出现网络中断时间过长等情况,会出现锁被意外释放的情况,这种危险比较难解决。

在释放锁时,也需要有重试机制。如果出现网络问题,锁也会在到期后释放,但是在释放前不能被其它进程获取。

相关推荐
盛派网络小助手1 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#
∝请叫*我简单先生1 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl
zquwei2 小时前
SpringCloudGateway+Nacos注册与转发Netty+WebSocket
java·网络·分布式·后端·websocket·网络协议·spring
dessler3 小时前
Docker-run命令详细讲解
linux·运维·后端·docker
Q_19284999063 小时前
基于Spring Boot的九州美食城商户一体化系统
java·spring boot·后端
ZSYP-S4 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
Yuan_o_4 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
程序员一诺5 小时前
【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】
后端·python
DT辰白5 小时前
如何解决基于 Redis 的网关鉴权导致的 RESTful API 拦截问题?
后端·微服务·架构