你是不是用redis来实现分布式锁?这些坑不得不防

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 在工作中,我们或多或少都用到过锁,今天我们就来讨论分布式场景下,我们可以通过哪种方式来解决锁的问题,这也是我在面试中经常遇到的一个问题,搞定他,非常重要。

前言


在工作中,我们或多或少都用到过锁,今天我们就来讨论分布式场景下,我们可以通过哪种方式来解决锁的问题,这也是我在面试中经常遇到的一个问题,搞定他,非常重要。


为什么需要锁


首先我们要搞懂,为什么需要锁?这是因为在同一时刻可能会有两个或两个以上的线程执行同一段代码,最经典的场景就是秒杀扣减库存,如果不对其加以控制,可能会出现期望之外的结果,比如超卖问题出现。

所以就算对并发编程没有系统学习过小伙伴,也会直接掏出万能方法-synchronized来,但是这种方式(JVM锁)只能解决单台服务器下的线程安全,如果是分布式场景下,这种方式肯定是无法满足的,这时候就需要用到分布式锁


如何实现分布式锁


实现分布式锁的方式有很多,我们常见的有数据库、redis、zookeeper,但无论哪种方式,其核心思想是共同的,就是同一时刻只能有一个线程能够获取到

比如现在有一个【下单系统】,分别在三台服务器上都部署一个实例,在同一时刻,每台服务器都想操作同一个订单的状态,但是这个时候只能有一台服务器能够操作成功,这时候就需要分布式锁的帮助了。


Redis分布式锁


redis用来做分布式锁是最常见的一种方式,之所以redis能够实现分布式锁,首先是因为它是单线程的,使用一个线程来处理所有的网络请求,因此也就不需要担心并发安全问题。

a2aa74eff02f886d0f5aff8f4c556d07.png

如上图,系统A在三台服务器分别部署一台实例,如果他们在同一时候都想修改某一个订单信息,那redis是通过哪种方式来实现分布式锁呢。

熟悉redis的小伙伴都知道,redis有一个命令【SET key 随机值 NX PX 1000】:

  • NX:当key不存在的时,会设置成功。
  • PX 1000:过期时间1000毫秒,当超过该时间,会自动释放。

通过这个命令,当第一个线程设置key时,redis服务返回OK,表示获取锁成功,在超时时间内,如果有其他服务器线程通过该命令,也来尝试获取锁时,redis服务会直接返回nil,表示当前锁被其他线程占用,获取失败。

77356b62cc30b73f5109d4f65638f4eb.png


面临的问题及解决


上述方式虽然可以满足分布式锁的需求,但是有几点问题需要我们注意:


第一点就是在设置value值时,必须使用随机值。


这是因为线程一拿到锁之后,在处理完自身业务后,会将该锁进行释放(主动删除redis中的key),但是有可能该线程阻塞了很长时间才处理完成,此时该可能已经被自动释放,并且被其他线程获取到,此时线程一直接删除key,必然会导致问题出现。

因此建议设置value为随机值,这样在删除key时通过lua脚本实现,删除前判断要删除的key的value值是否与当前线程的value值相同,只有在相同情况下才进行删除操作。


第二点就是redis单点故障。


因为如果是普通的redis单实例,那就是单点故障。或者是redis普通主从,那redis主从异步复制,如果主节点挂了,key还没同步到从节点,此时从节点切换为主节点,别人就会拿到锁。


RedLock原理介绍


该方案是redis官方支持的分布式锁算法,也是对上述方式的优化,这里花哥简单介绍一下原理。

93b6c45a906f1b09179cd7002c306f2c.png

如上图,假设现在有5个redis节点,节点之间相互独立,彼此之间不进行同步,某个线程如果想要成功获取到锁,需要完成以下几个步骤:

  1. 获取当前时间戳,单位是毫秒;
  2. 使用相同的key和随机value,轮流尝试在每个master节点上创建锁;
  3. 当且仅当从大多数(N/2+1,这里是3个节点)的Redis节点都取到锁,并且获取锁使用的时间小于锁失效时间时,锁才算获取成功。;
  4. 客户端计算建立好锁的时间,如果建立锁的时间小于超时时间,就算建立成功了;
  5. 如果由于某些原因未能获得锁,比如无法在至少N/2+1个Redis实例获取锁或获取锁的时间超过了有效时间,客户端应该在所有的Redis实例上进行解锁。


RedLock存在的问题


  1. 如果线程1从3个实例获取到了锁,但是这3个实例中的某个实例的系统时间走得稍微快一点,则它持有的锁会提前过期被释放,当他释放后,此时又有3个实例是空闲的,则线程2也可以获取到锁,则可能出现两个线程同时持有锁了。
  2. 如果线程1从3个实例获取到了锁,但是万一其中有1台重启了,则此时又有3个实例是空闲的,则线程2也可以获取到锁,此时又出现两个线程同时持有锁了。


总结


今天和大家分享了分布式锁中redis的实现方式,介绍了它的实现原理和需要注意点,不过说实话,redis用来做分布式锁个人认为并不是很完美,一般我也不这么用。至于为什么,除了redis自身的不足外,还要和其他实现方式进行对比取舍。

相关实践学习
基于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 算法 安全
Redlock 算法-主从redis分布式锁主节点宕机锁丢失的问题
Redlock 算法-主从redis分布式锁主节点宕机锁丢失的问题
154 0
|
1月前
|
NoSQL 关系型数据库 MySQL
分布式锁(redis/mysql)
分布式锁(redis/mysql)
58 1
|
29天前
|
NoSQL Java Redis
如何通俗易懂的理解Redis分布式锁
在多线程并发的情况下,我们如何保证一个代码块在同一时间只能由一个线程访问呢?
37 2
|
1月前
|
人工智能 监控 NoSQL
【万字长文 一文搞定】Redis:从新手村到大师殿堂的奥德赛之旅 9种实现分布式锁的全技术指南
【万字长文 一文搞定】Redis:从新手村到大师殿堂的奥德赛之旅 9种实现分布式锁的全技术指南
81 4
|
1月前
|
消息中间件 存储 NoSQL
【Redis项目实战】使用Springcloud整合Redis分布式锁+RabbitMQ技术实现高并发预约管理处理系统
【Redis项目实战】使用Springcloud整合Redis分布式锁+RabbitMQ技术实现高并发预约管理处理系统
|
1月前
|
NoSQL API Redis
Redis分布式锁实现的三个核心
Redis分布式锁实现的三个核心
|
1月前
|
NoSQL Java Redis
Redis分布式锁和Java锁的区别
Redis分布式锁和Java锁的主要区别在于它们的适用范围和实现机制。
41 2
|
15天前
|
NoSQL Linux Redis
06- 你们使用Redis是单点还是集群 ? 哪种集群 ?
**Redis配置:** 使用哨兵集群,结构为1主2从,加上3个哨兵节点,总计分布在3台Linux服务器上,提供高可用性。
216 0
|
23天前
|
负载均衡 监控 NoSQL
Redis的集群方案有哪些?
Redis集群包括主从复制(基础,手动故障恢复)、哨兵模式(自动高可用)和Redis Cluster(官方分布式解决方案,自动分片和容错)。此外,还有如Codis、Redisson和Twemproxy等第三方工具用于代理和负载均衡。选择方案需考虑应用场景、数据规模和并发需求。
181 2