你是不是用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
相关文章
|
2天前
|
NoSQL Java 关系型数据库
【Redis系列笔记】分布式锁
分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路
129 2
|
2天前
|
存储 监控 NoSQL
【Redis】分布式锁及其他常见问题
【Redis】分布式锁及其他常见问题
16 0
|
2天前
|
NoSQL Java Redis
【Redis】Redis实现分布式锁
【Redis】Redis实现分布式锁
7 0
|
2天前
|
监控 NoSQL 算法
探秘Redis分布式锁:实战与注意事项
本文介绍了Redis分区容错中的分布式锁概念,包括利用Watch实现乐观锁和使用setnx防止库存超卖。乐观锁通过Watch命令监控键值变化,在事务中执行修改,若键值被改变则事务失败。Java代码示例展示了具体实现。setnx命令用于库存操作,确保无超卖,通过设置锁并检查库存来更新。文章还讨论了分布式锁存在的问题,如客户端阻塞、时钟漂移和单点故障,并提出了RedLock算法来提高可靠性。Redisson作为生产环境的分布式锁实现,提供了可重入锁、读写锁等高级功能。最后,文章对比了Redis、Zookeeper和etcd的分布式锁特性。
134 16
探秘Redis分布式锁:实战与注意事项
|
2天前
|
NoSQL Java 大数据
介绍redis分布式锁
分布式锁是解决多进程在分布式环境中争夺资源的问题,与本地锁相似但适用于不同进程。以Redis为例,通过`setIfAbsent`实现占锁,加锁同时设置过期时间避免死锁。然而,获取锁与设置过期时间非原子性可能导致并发问题,解决方案是使用`setIfAbsent`的超时参数。此外,释放锁前需验证归属,防止误删他人锁,可借助Lua脚本确保原子性。实际应用中还有锁续期、重试机制等复杂问题,现成解决方案如RedisLockRegistry和Redisson。
|
2天前
|
缓存 NoSQL Java
【亮剑】分布式锁是保证多服务实例同步的关键机制,常用于互斥访问共享资源、控制访问顺序和系统保护,如何使用注解来实现 Redis 分布式锁的功能?
【4月更文挑战第30天】分布式锁是保证多服务实例同步的关键机制,常用于互斥访问共享资源、控制访问顺序和系统保护。基于 Redis 的分布式锁利用 SETNX 或 SET 命令实现,并考虑自动过期、可重入及原子性以确保可靠性。在 Java Spring Boot 中,可通过 `@EnableCaching`、`@Cacheable` 和 `@CacheEvict` 注解轻松实现 Redis 分布式锁功能。
|
2天前
|
NoSQL Redis 微服务
分布式锁_redis实现
分布式锁_redis实现
|
机器学习/深度学习 缓存 NoSQL
|
缓存 NoSQL Java
为什么分布式一定要有redis?
1、为什么使用redis 分析:博主觉得在项目中使用redis,主要是从两个角度去考虑:性能和并发。当然,redis还具备可以做分布式锁等其他功能,但是如果只是为了分布式锁这些其他功能,完全还有其他中间件(如zookpeer等)代替,并不是非要使用redis。
1334 0
|
2天前
|
负载均衡 监控 NoSQL
Redis的几种主要集群方案
【5月更文挑战第15天】Redis集群方案包括主从复制(基础,读写分离,手动故障恢复)、哨兵模式(自动高可用,自动故障转移)和Redis Cluster(官方分布式解决方案,自动分片、容错和扩展)。此外,还有Codis、Redisson和Twemproxy等工具用于代理分片和负载均衡。选择方案需考虑应用场景、数据量和并发需求,权衡可用性、性能和扩展性。
29 2

热门文章

最新文章