redis 分布式锁

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介:

基于redis的分布式锁(redlock)

在分布式系统逐渐变为主流的情况下,开发者会遇到很多在单机系统中不会遇到的问题,而如何实现一个健壮的分布式锁就是很多开发者要面临的一个问题。

分布式锁的实现方式有很多种,本文将讲述一种基于redis实现的分布式锁(至于网上博客上说的那种仅仅利用SETNX命令去实现锁的,这种在单机下还行,如果需要在分布式环境下使用,呵呵~)。

PS:本锁的实现是官方提供的Redlock算法(其实就是翻译了一下,相较于谷歌翻译就是更易读,虽然这看起来没什么,但是很多时候这将最终影响你是否能够理解该算法)

首先,我们列举出满足我们需求的分布式锁的基本要素:

1、安全特性:相互排斥。也就是说在任何时刻,只有一个客
户端可以锁定而不会发生锁被两个客户端同时锁定。
2、活性A:无死锁。也就是说,无论什么情况,例如锁定资源    的客户端崩溃或者网络被分裂也总是可以获取锁的。
3、活性B:容错,只要大多数redis节点启动,客户端就可以获    取并释放锁。

然后我们看为什么基于故障切换(master-slave模型,master故障时slave升级为master)的实现是不够的:

使用redis锁定资源最简单的方法是在redis实例中创建一个密        钥,关键是通常使用redis过期功能实现在客户端崩溃后锁的自    动释放。

看起来该实现很好,但是存在这样一个问题:该实现存在单点故    障。考虑这样一个场景,如果redis-master挂掉会怎么样呢?为    了防止redis-master挂掉,我们添加一个slave,如果master挂    掉就切换到slave节点,但很不幸的是,这样是不可行的,通过    这样做无法实现上边的互斥安全属性,因为redis复制是异步        的。
上述模型有一个明显的竞争条件:
    1、客户端A获取master设备中的锁定;
    2、在将密钥写入slave节点前master崩溃;
    3、slave升级为master;
    4、客户端B使用同样的密钥获取客户端A本已获取到的资源        锁;
这样就发生了互斥安全问题!

那么如何解决上述问题呢?我们可以这样做:

在尝试克服单实例的节点故障问题前,让我们在这种简单的情况    下检查如何正确的执行它,因为这实际上是一个可执行的方案,    在    应用程序中不时会出现争用情况,并且实现单实例redis的    锁定是我    们描述分布式算法的基础;

要获得锁定,要走的路线如下:
SET resource_name my_random_value NX PX 30000
该命令只在密钥不存在时才会成功设置(NX选项),并且过期    时间为30000毫秒(PX选项)。该键被设置为一个随机值,该随    机值在所有的客户端和所有对该资源的锁定请求中必须唯一(否    则会出现互斥问题,下面会介绍)。

基于随机值是为了以安全的方式释放锁,使用随机值,通过脚本    告诉redis:只有当密钥存在并且存储在密钥中的值恰好与我期    望的值(随机值)一样时,才能移除该密钥。该过程是很重要        的,主要是为了防止一个客户端移除其他客户端创建的锁。例        如:客户端A当前获取到了锁,但是客户端A并不是因为崩溃而    超时,只是被阻塞超时,例如进行数据库操作时被阻塞,所以等    到客户单A阻塞结束后,他会认为他还持有锁,然后会通过DEL    命令尝试释放锁,但是实际上该锁此时是客户端B所有的,而客    户端A并不知道,这样就会造成客户端A将客户端B的锁删除而造    成客户端B锁失效,并且后续客户端B可能还会重复该操作,此    时锁实际上已经失去了作用,而如果加入随机数后,客户端A删    除的时候会校验该随机数,如果发现该随机数与客户端A设置的    不一致时则表示该锁已经不是A所有的,则A不会执行DEL操作,    B仍然会持有锁继而保持互斥安全,而这也是为什么要保证随机    数唯一的原因。

现在我们有了一个很好的方法来获得和释放锁,该系统对于由一    个总是可用的redis实例组成的非分布式系统(redis是非分布式    的)是安全的,现在我们尝试将该概念扩展到一个分布式系统。

在上述算法的分布式版本中,我们假设我们有N个redis-master    节点,这些节点完全独立,所以我们不使用复制或任何其他隐式    协调系统。我们已经描述了如何在单个实例中安全的获取和释放    锁,我们理所当然的认为该算法将使用此方法在单个示例中获取    并释放锁。在我们的例子中,我们设置N=5,这是一个合理的    值,所以我们需要在不同的计算机或虚拟机上运行5个redis-ma    ster节点,以确保他们以最独立的方式失败(单个节点挂掉不会    影响其他节点)。

为了获得锁,客户端执行以下操作:
    1、它以毫秒为单位获取当前时间;
    2、它试图依次获取所有N个实例中的锁,在所有实例中使用        相同的密钥名和随机值。在步骤2中,当在每个实例中设置锁        时,客户端使用与锁自动释放时间相比较小的超时以获取它,        例如:如果自动释放时间是10秒,则超时可能在5至50毫秒范        围内。这样当某个redis挂掉后可以防止客户端长时间阻塞在        尝试与该redis实例创建连接的状态,如果redis实例不可用,        那么我们应该尽快尝试与下一个redis实例通讯。
    3、客户机通过从当前时间中扣除步骤1中获得的时间戳来计        算获取锁定所花费的时间。当且仅当客户机能获得大多数redi        s实例的锁定(至少N/2+1个redis实例的锁定),并且获取该        锁的总时间小于锁有效时间,则认为该锁被获取;
    4、如果获得锁,则其有效时间被认为是初始有效时间减去经        过时间,该经过时间如步骤3中计算的那样。(因为如果超过        该时间那么获取的第一个redis实例的锁将会超时自动释                放),该锁将有可能不满足[至少N/2+1个redis实例的锁定]        条件;
    5、如果客户端由于某种原因未能获得锁(或者无法锁定N/2        +1个实例或者获得的锁有效时间为负),他将尝试解锁所有        实例(即使他认为他没有获取到锁,方便后续其他客户端重新        获取锁);

算法是异步的吗?

上述算法依赖于这样一个假设:即虽然跨进程没有同步时钟,但    每个进程中的本地时间仍以大约相同的速率步进,与锁的自动释    放时间相比误差较小。这个假设与真实世界的计算机非常相似,    每台计算机都有一个本地时钟,我们通常可以依靠不同的计算机    来产生很小的时钟漂移。

在这一点上,我们需要更好地指定我们的互斥规则:即锁的有效    期只有持有锁的客户端在有效时间(有效时间为步骤3中获得        的)减去某个时间(仅几毫秒以补偿不同主机之间的时钟漂        移)。

参考文献

[2]H. Berenson, P. Bernstein, J. Gray, J.Melton, E. O’Neil,and P. O’Neil. A critique of ANSI SQL isolation levels. InProceedings of the SIGMOD International Conference on Management of Data, pages1–10, May 1995.

[3]Michael J. Cahill, Uwe Röhm, and Alan D.Fekete. 2008. Serializable isolation for snapshot databases. In SIGMOD ’08:Proceedings of the 2008 ACM SIGMOD international conference on Management of data, pages 729–738, New York, NY, USA. ACM.

[4]Michael James Cahill. 2009. Serializable Isolation for Snapshot Databases. Sydney Digital Theses. University of Sydney, School of Information Technologies

[5] A. Fekete, D. Liarokapis, E. O’Neil, P.O’Neil, andD. Shasha. Making snapshot isolation serializable. www.codexueyuan.com In ACM transactions on database systems, volume 39(2), pages 492–528, June 2005.
版权声明:本文内容

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
1月前
|
NoSQL Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
|
13天前
|
NoSQL Redis
Redis分布式锁如何实现 ?
Redis分布式锁通过SETNX指令实现,确保仅在键不存在时设置值。此机制用于控制多个线程对共享资源的访问,避免并发冲突。然而,实际应用中需解决死锁、锁超时、归一化、可重入及阻塞等问题,以确保系统的稳定性和可靠性。解决方案包括设置锁超时、引入Watch Dog机制、使用ThreadLocal绑定加解锁操作、实现计数器支持可重入锁以及采用自旋锁思想处理阻塞请求。
50 16
|
1月前
|
缓存 NoSQL Java
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
61 3
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
|
1月前
|
NoSQL Redis 数据库
计数器 分布式锁 redis实现
【10月更文挑战第5天】
50 1
|
1月前
|
NoSQL 算法 关系型数据库
Redis分布式锁
【10月更文挑战第1天】分布式锁用于在多进程环境中保护共享资源,防止并发冲突。通常借助外部系统如Redis或Zookeeper实现。通过`SETNX`命令加锁,并设置过期时间防止死锁。为避免误删他人锁,加锁时附带唯一标识,解锁前验证。面对锁提前过期的问题,可使用守护线程自动续期。在Redis集群中,需考虑主从同步延迟导致的锁丢失问题,Redlock算法可提高锁的可靠性。
80 4
|
1月前
|
缓存 NoSQL Ubuntu
大数据-39 Redis 高并发分布式缓存 Ubuntu源码编译安装 云服务器 启动并测试 redis-server redis-cli
大数据-39 Redis 高并发分布式缓存 Ubuntu源码编译安装 云服务器 启动并测试 redis-server redis-cli
56 3
|
1月前
|
缓存 NoSQL 算法
面试题:Redis如何实现分布式锁!
面试题:Redis如何实现分布式锁!
|
3月前
|
NoSQL Redis
基于Redis的高可用分布式锁——RedLock
这篇文章介绍了基于Redis的高可用分布式锁RedLock的概念、工作流程、获取和释放锁的方法,以及RedLock相比单机锁在高可用性上的优势,同时指出了其在某些特殊场景下的不足,并提到了ZooKeeper作为另一种实现分布式锁的方案。
113 2
基于Redis的高可用分布式锁——RedLock
|
3月前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
这篇文章是关于如何在SpringBoot应用中整合Redis并处理分布式场景下的缓存问题,包括缓存穿透、缓存雪崩和缓存击穿。文章详细讨论了在分布式情况下如何添加分布式锁来解决缓存击穿问题,提供了加锁和解锁的实现过程,并展示了使用JMeter进行压力测试来验证锁机制有效性的方法。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
|
1月前
|
存储 缓存 NoSQL
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
64 4
下一篇
无影云桌面