redis分布式锁可能出现的问题(redis分布式锁存在的问题)

本篇文章给大家谈谈redis分布式锁可能出现的问题,以及redis分布式锁存在的问题对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

Redis 分布式锁有什么缺陷?

Redis 分布式锁不能解决超时的问题,分布式锁有一个超时时间,程序信森的执行族坦芹如兆毕果超出了锁的超时时间就会出现问题。

真正的 Redis 分布式锁,就该是这样实现的

众所周知,redis 分布式锁使用 SET 指令可以实现,但是仅仅使用该命令就行了吗?是否还需要考虑 CAP 理论。

要是有上面说的那么简单就好喽,我们平时在开发中用到的分布式锁方案可能比较简单,这个取决于业务的复杂程度以及并发量。

下面我们来说说在高并发场景中,该如何正确使用分布式锁。

在正式讲解分布式锁之前,先来看下将要围绕展开来讲的几个问题:

在同一时刻,只能有一个线程去读写一个【共享资源】,也就是高并发的场景下,通常为了保证数据的正确,需要控制同一时刻只允许一个线程访问。

此时就需要使用分布式锁了。

简而言之,分布式锁就是用来控制同一时刻,只有一个线程可以访问被保护的资源。

可信肆以使用 SETNX key value 命令实现互斥的特性。

解释下:如果 key 不存在,则设置 value 给这个 key ,否则啥都不做。

该命令的返回值有如下两种情况:

举例如下:

成功的情况:

SETNX lock:101 1 (integer) 1 # 获取 101 锁 成功

失败的情况:

SETNX lock:101 2 (integer) 0 # 后面申请锁 获取失败

可见,成功的就可以开始使用「共享资源」了。

使用结束后,要及时释放锁,给后面申请获得资源的机会。

释放锁比较简单,使用 DEL 命令删除这个 key 就可以了。如下:

DEL lock:101 (integer) 1

分布式锁简单使用方案如下:

这看起来不是挺简单的吗,能有什么问题?往下听我分析。

首先该方案存在一个锁无法被释放的问题,场景如下:

可见,这个锁就会一直被占用,导致其它客户端也拿不到这个锁了。

设置举例如下:

SETNX lock:101 1 // 获取锁 (integer) 1

EXPIRE lock:101 60 // 60s 过期删除 (integer) 1

可见,60 秒后后该锁就好释放掉,其他客户就可以申请使用察凳了。

由上面举例可知: 加锁 和 设置过期时间 是两个操作命令,并不是原子操作。

试想一下,可能存在这么个情况:

比如执行第一条命了成功,第二条命令还没来得及执行就出现了异常,导致设置 「 过期时间」失败,这样锁也是无法释放。

SET keyName value NX PX 30000

这样一看,似乎没啥毛病。不,仔细一看,写的还是不够严谨。想下,有没可能释放的不是自己加的锁。

思考中……

说下什么场景下, 释放的锁不是自己的 :

所以,有个关键点需要注意的是:只能释放自己申请的锁。

总之,解铃还须系铃人

伪代码如下:

// 判断 value 与 锁的唯一标识

此时,我们可以考虑通过 Lua 脚本来实现,这样判断和删除的过滑没轿程就是原子操作了。

// 获取锁的 value 值与 ARGV[1] 比较,匹配成功则执行 del

使用上面的脚本,为每个锁分配一个随机字符串“签名”,只有当删除锁的客户端的“签名”与锁的 value 匹配的时候,才会去删除它。

遇到问题不要慌,先从官方文档入手:redis.io/topics/dist…

到目前为止,以上修改后(优化后)的方案算相比较完善的了,业界大部分使用的也都是该方案。

当然这个锁的过期时间不能瞎写,通常是根据多次测试后的结果来做选择,比如压测多轮之后,平均执行时间在 300 ms。

那么我们的锁 过期时间就应该放大到平均执行时间的 3~4 倍。

有些小伙伴可能会问:为啥是放大 3~4 倍 呢 ?

这叫凡事留一手:考虑到锁的操作逻辑中如果有网络 IO 操作等,线上的网络不会总是稳定的,此时需要留点时间来缓冲。

加锁的时候设置一个过期时间,同时客户端开启一个「守护线程」,定时去检测这个锁的失效时间。

如果快要过期,但是业务逻辑还没执行完成,自动对这个锁进行续期,重新设置过期时间。

可以先谷歌一下,相信谷歌大哥会告诉你有这么一个库把这些工作都封装好了,你只管用就是了,它叫 Redisson 。

在使用分布式锁的时候,其实就是采用了「自动续期」的方案来避免锁过期,这个守护线程我们一般也把它叫做「看门狗」线程。

这个方案可以说很 OK 了,能想到这些的优化点已经击败一大批程序猿了。

对于追求极致的程序员来说,你们可能会考虑到:

这里就不展开讨论了。有兴趣的可以在评论区一起讨论交流哈。

[img]

redis分布式锁常见问题及解决方案

        1.1 锁需要具备唯一性

        1.2 锁需要有超时时间,防止死锁

        1.3 锁的创建和设置锁超时时间需要具备原子性

        1.4 锁的超时的续期问题

       虚尘 1.5 B的锁被A给释放了的问题

        1.6 锁的可重入问题

        1.7 集群下分布式锁的问题

        问题讲解:

        首先分布式锁要解决的问题就是毁枯分布式环境下同一资源被多个进程进行访问和操作的问题,既然是同一资源,那么肯定要考虑数据安全问题.其实和单进程下加锁解锁的原理是一样的,单进程下需要考虑多线程对同一变量进行访问和修改问题,为了保证同一变量不被多个线程同时访问,按照顺序对变量进行修改,就要在访问变量时进行加锁,这个加锁可以是重量级锁,也可以是基于cas的乐观锁.

        解决方案:

        使用redis命令setnx(set if not exist),即只能被一个客户端占坑,如果redis实例存在唯一键(key),如果再想在该键(key)上设置值,就会被拒绝.

        问题讲解:

        redis释放锁需要客户端的操作,如果此时客户端突然挂了,就没有释放锁的操作了,也意味着其他客户端想要重新加锁,却加不了的问题.

        解决方案:

        所以,为了避免客户端挂掉或者说是客户端不能正常释放锁的问题,需要在加锁的同时,给锁加上超时时间.

        即,加锁和给锁加上超时时间的操作如下操作:

setnx lockkey true    #加锁操作

ok

expire lockkey 5    #给锁加上超时时间

... do something critical ...

del lockkey    #释放锁

(integer) 1

        问题讲解:

        通过2.3加锁和超时时间的设置可以看到,setnx和expire需要两个命令来完成操作,也就是需要两次RTT操作,如果在setnx和expire两次命令之间,客户端突然挂掉,这时又无法释放锁,且又回到了死锁的问题.

        解决方案:

        使用set扩展命令

        如下:

set lockkey true ex 5 nx   #加锁,过期时间5s

ok

... do something critical ...

del lockkey

        以上的set lockkey true ex 5 nx命令可以一次性完成setnx和expire两个操作,也就是解决了原子性问题.

        问题讲解:

        redis分布式锁过期,而业务逻辑没执行完的场景,不过,这里换一种思路想问题,把redis锁的过期时间再弄长点不就解决了吗?那还是有问题,我们可以在加锁的时候,手动调长redis锁的过期时间,可这个时间多长合适?业务逻辑的执行时间是不可控的,调的过长又会影响操作性能。

        解决方案:

        使用redis客户端redisson,redisson很好的解决了redis在分布式环境下的一些棘手问题,它的宗旨就是让使用者减少对Redis的关注,将更多精力用在处理业务逻辑上。redisson对分布式锁做了很好封装,只需调用API即可。RLock lock = redissonClient.getLock("stockLock");

        redisson在加锁成功后,会注册一个定时任务监听这个锁,每隔10秒就去查看这个锁,如果还持有锁,就对过期时间进行续期。默认过期时间30秒。这个机制也被叫做:“看门狗”

        问题讲解:

        A、B两个线程来尝试给key myLock加锁,A线程先拿到锁(假如锁3秒后过期),B线程就在等待尝试获取锁,到这一点毛病没有。那如果此时业务逻辑比较耗时,执行时间已经超过redis锁过期时间,这时A线程的锁自动释放(删除key),B线程检测到myLock这个key不存在,执行 SETNX命令也拿到了锁。但是,此时A线程执行完业务逻辑之后,还是会去释放锁(删除key),这就导差余禅致B线程的锁被A线程给释放了。

      解决方案:

      一般我们在每个线程加锁时要带上自己独有的value值来标识,只释放指定value的key,否则就会出现释放锁混乱的场景一般我们可以设置value为业务前缀_当前线程ID或者uuid,只有当前value相同的才可以释放锁

        问题讲解:

        上面我们讲了,为了保证锁具有唯一性,需要使用setnx,后来为了与超时时间一起设置,我们选用了set命令。 在我们想要在加锁期间,拥有锁的客户端想要再次获得锁,也就是锁重入

        解决方案:

       给锁设置hash结构的加锁次数,每次加锁就+1

        问题讲解:

        这一问题是在redis集群方案时会出现的.事实上,现在为了保证redis的高可用和访问性能,都会设置redis的主节点和从节点,主节点负责写操作,从节点负责读操作,也就意味着,我们所有的锁都要写在主redis服务器实例中,如果主redis服务器宕机,资源释放(在没有加持久化时候,如果加了持久化,这一问题会更加复杂),此时redis主节点的数据并没有复制到从服务器,此时,其他客户端就会趁机获取锁,而之前拥有锁的客户端可能还在对资源进行操作,此时又会出现多客户端对同一资源进行访问和操作的问题.

        解决方案:

        使用redlock,原理与zookeeper分布式锁原理相同.多台主机超过半数设置成功则获取锁成功,要注意下主机个数必须是奇数,不过这有效率问题

缓存击穿、穿透、雪崩及Redis分布式锁

分布式锁: setnx ,redisson 并发问题

幂等问题: 落表状态,Redis

缓存击穿: 指缓存中无,db中有

原因: 一个key高并发恰好失效导致大量请求到db

方案: 加锁,自旋锁,或一个线程查芹乎db,一个线程监控(直接用Redisson分布式锁)

缓存穿透:指缓存和db中均无

原因: 一般是恶意请求

方案: 加布隆过滤,或查db无时,也设置缓存,value为某些特殊表示或"null"

雪崩:指缓存同时大量失效

原因: 大量的key同时失效,db压力加大

方案: 设置失效时间是增加随机数

问题方案文献:

(图例洞首简分析)

Redis分布式锁:

事务未执行完锁已到期释放问题:使用Redissoin解决续租问题,内部已解决

分布式锁文献:

(setnx + expire同时操作)

====================================

Redis原理与应纳裤用

分布式Redis的分布式锁 Redlock

引言

之前自己在用redis来实现分布式锁的时候都是基于单个Redis实例,也就是说Redis本身是有单点故障的,Redis的官方文档介绍了一种"自认为"合理的算法,Redlock来实现分布式Redis下的分布式锁。

Martin Kleppmann写了一篇文章分析Redlock。然后redis的作者写了一篇反驳的文章这里。加油。

Redlock实现库

单点Redis锁

先简单回顾一下单点的Redis锁是怎么实现的。

获取锁

客户端A在Redis上设置一个特定的键值对,同时给一个超时时间(避免死锁)。其他客户端在访问的时候先看看这个key是否已经存在,并且值等于my_random_value。如果已存在就等待,否则就获取成功,执行业务代码。resource_name和my_random_value是所有客户端都知道并且共享的。

释放锁

对比key获取到的对应的value是否相等,如果相等,就删除(释放),否则就返回失败。

之前也写过一篇文章氏漏携。

单搜宽点Redis锁的缺陷

这个缺陷其实很明显,如果只有一个Redis实例,这个挂了,所有依赖他的服务都挂了。显然不太适合大型的应用。

简单的Redis主从架构碰到的问题

为了避免单点故障,我们给Redis做一个Master/Slave的主从架构,一个Master,一台Slave。下面就会碰到这么一个问题。下面是使用场景。

Redlock算法

假设我们有N(假设5)个Redis master实例,所有歼伏节点相互独立,并且业务系统也是单纯的调用,并没有什么其他的类似消息重发之类的辅助系统。下面来模拟一下算法:

锁的释放

分布式锁

与分布式锁对应的是【单机锁】,我们在写多线程程序时,避免同时操作一个共享变量而产生数据问题,通常会灶段使用一把锁来实现【互斥】,其使用范围是在【同一个进程中】。(同一个进程内存是共享的,以争抢同一段内存,来判断是否抢到锁)。

如果是多个进程,如何互斥呢。就要引入【分布式锁】来解决这个问题。想要实现分布式锁,必须借助一个外部系统,所有进程都去这个系统上去【申请加锁】。

而这个外部系统,必须要实现【互斥】的能力,即两个请求同时进来,只会给一个进程返回成功,另一个返回失败(或等待)。

这个外部系统,可以是 MySQL,也可以是 Redis 或 Zookeeper。但为了追求更好的性能,我们通常会选择使用 Redis 或 Zookeeper 来做。

依赖mysql的行锁 select for update。

一个特例,唯一索引。唯一索引是找到了就直接停止遍历,非唯一索引还会向后遍历一行。移步第八个case。

现在的索引:

分析:

单条命中只会加行锁,不加间隙锁。所以RC/RR是一样的。

事务1对id=10这条记录加行锁。所以 场景1会锁等待,场景2不会锁等待 。

分析:

RC隔离级别:

事务1未命中,不会加任何锁。所以 场景1,场景2,场景3都不会锁等待 。

RR隔离级别:

事务1未命中,会加间隙锁。因为主键查询,只会对主键加锁。

在10和18加间隙锁。

间隙锁和查询不冲突。 场景1不会锁等待 。

间隙锁和插入冲突。 场景2和场景3会锁等待 。

分析

单条命中只会加行锁,不加间隙锁。所以RC/RR是一样的。

事务1对二级索引和主键索引加行锁。 事务1和事务2都会发生锁等待 。

分析

RC隔离级别:

事务1未命中,不会加任何锁。所以 场景1,场景2,场景3都不会锁等待 。

RR隔离级别:

事务1对二级索引N0007到正无穷上间隙锁,主键索引不上锁。 场景1会锁等待,场景2不会锁等待 。

分析:

RC隔离级别:

只会加行锁。 场景1场景2会锁等待 。 场景3不会发生锁等待 。

RR隔离级别:

会加行锁和间隙锁。 场景1场景2场景3都会锁等待 。

ps: 如果是唯一索引,只会加行锁。非唯一才会加间隙锁。

RC隔离级别:

事务1未命中,不会加任何锁。所以 场景1,场景2都不会锁等待 。

RR隔离级别:

事务1未命中,会加间隙锁。间隙锁与查询不冲突, 场景1不会发生锁等待 。 场景2会发生锁等待 。

分析

RC隔离孙孙级别:

事务1加了三个行锁。 场景1会锁等待。场景2,场景3不会发生锁等待 。

RR隔离级别:

事务1加个三个行锁和间隙锁。 场景1,场景3会发生锁等待 。间隙锁与查询不则辩链冲突, 场景2不会锁等待。

分析:

RC隔离级别:

事务1加的都是行锁。 场景1会发生锁等待 , 场景2,场景3不会发生锁等待 。

RR隔离级别:

事务1会对二级索引加行锁和间隙锁,对主键索引加行锁。

场景1,场景3会发生锁等待 。间隙锁与查询不冲突, 场景2不会锁等待。

这么看,二级索引和唯一索引没什么区别。

那如果是 select * from book where name 'Jim' for update; 呢

如果name是唯一索引。因为找到jim就不会向后遍历了,所以jim和rose之间不会有间隙锁。

分析:

RC隔离级别:

由于没有走索引,所以只能全表扫描。在命中的主键索引上加行锁。 场景1会锁等待,场景2不会锁等待 。

RR隔离级别:

不开启innodb_locks_unsafe_for_binlog。 会发生锁表 。

开启innodb_locks_unsafe_for_binlog。和RC隔离级别一样。

RC隔离级别:

未命中不加锁。 场景1,场景2都不会锁等待 。

RR隔离级别:

未命中, 锁表 。

在RR隔离级别下,where条件没有索引,都会锁表。

加锁命令:

释放锁命令:

这里存在问题,当释放锁之前异常退出了。这个锁就永远不会被释放了。

怎么解决呢?加一个超时时间。

还有问题,不是原子操作。

redis 2.6.12之后,redis天然支持了

来看一下还有什么问题:

试想这样一个场景

看到了么,这里存在两个严重的问题:

释放别人的锁 :客户端 1 操作共享资源完成后,却又释放了客户端 2 的锁

锁过期 :客户端 1 操作共享资源耗时太久,导致锁被自动释放,之后被客户端 2 持有

解决办法是:客户端在加锁时,设置一个只有自己知道的【唯一标识】进去。

例如,可以是自己的线程id,也可以是一个uuid

在释放锁时,可以这么写:

问题来了,还不是原子的。redis没有原生命令了。这里需要使用lua脚本

锁的过期时间如果评估不好,这个锁就会有「提前」过期的风险,一般的妥协方案是,尽量「冗余」过期时间,降低锁提前过期的概率。

其实可以有比较好的方案:

加锁时,先设置一个过期时间,然后我们开启一个「守护****线程****」,定时去检测这个锁的失效时间,如果锁快要过期了,操作共享资源还未完成,那么就自动对锁进行「续期」,重新设置过期时间。

这个守护线程我们一般把他叫做【看门狗】线程。

我们在使用 Redis 时,一般会采用 主从集群 + 哨兵 的模式部署,这样做的好处在于,当主库异常宕机时,哨兵可以实现「故障自动切换」,把从库提升为主库,继续提供服务,以此保证可用性。

试想这样的场景:

为此,Redis 的作者提出一种解决方案,就是我们经常听到的 Redlock(红锁) 。

现在我们来看,Redis 作者提出的 Redlock 方案,是如何解决主从切换后,锁失效问题的。

Redlock 的方案基于 2 个前提:

也就是说,想用使用 Redlock,你至少要部署 5 个 Redis 实例,而且都是主库,它们之间没有任何关系,都是一个个孤立的实例。

Redlock 具体如何使用呢?

整体的流程是这样的,一共分为 5 步:

有 4 个重点:

1) 为什么要在多个实例上加锁?

本质上是为了「容错」,部分实例异常宕机,剩余的实例加锁成功,整个锁服务依旧可用。

2) 为什么大多数加锁成功,才算成功?

多个 Redis 实例一起来用,其实就组成了一个「分布式系统」。

在分布式系统中,总会出现「异常节点」,所以,在谈论分布式系统问题时,需要考虑异常节点达到多少个,也依旧不会影响整个系统的「正确性」。

这是一个分布式系统「容错」问题,这个问题的结论是: 如果只存在「故障」节点,只要大多数节点正常,那么整个系统依旧是可以提供正确服务的。

3) 为什么步骤 3 加锁成功后,还要计算加锁的累计耗时?

因为操作的是多个节点,所以耗时肯定会比操作单个实例耗时更久,而且,因为是网络请求,网络情况是复杂的,有可能存在 延迟、丢包、超时 等情况发生,网络请求越多,异常发生的概率就越大。

所以,即使大多数节点加锁成功,但如果加锁的累计耗时已经「超过」了锁的过期时间,那此时有些实例上的锁可能已经失效了,这个锁就没有意义了。

4) 为什么释放锁,要操作所有节点?

在某一个 Redis 节点加锁时,可能因为「网络原因」导致加锁失败。

例如,客户端在一个 Redis 实例上加锁成功,但在读取响应结果时,网络问题导致 读取失败 ,那这把锁其实已经在 Redis 上加锁成功了。

所以,释放锁时,不管之前有没有加锁成功,需要释放「所有节点」的锁,以保证清理节点上「残留」的锁。

好了,明白了 Redlock 的流程和相关问题,看似 Redlock 确实解决了 Redis 节点异常宕机锁失效的问题,保证了锁的「安全性」。

在martin的文章中,主要阐述了4个论点:

第一:效率 。

使用分布式锁的互斥能力,是避免不必要地做同样的工作两次。如果锁失效,并不会带来「恶性」的后果,例如发了 2 次邮件等,无伤大雅。

第二:正确性 。

使用锁用来防止并发进程相互干扰。如果锁失败,会造成多个进程同时操作同一条数据,产生的后果是数据严重错误、永久性不一致、数据丢失等恶性问题,后果严重。

他认为,如果你是为了前者——效率,那么使用单机版redis就可以了,即使偶尔发生锁失效(宕机、主从切换),都不会产生严重的后果。而使用redlock太重了,没必要。

而如果是为了正确性,他认为redlock根本达不到安全性的要求,也依旧存在锁失效的问题!

一个分布式系统,存在着你想不到的各种异常。这些异常场景主要包括三大块,这也是分布式系统会遇到的三座大山: NPC

martin用一个进程暂停(GC)的例子,指出了redlock安全性的问题:

又或者,当多个Redis节点时钟发生了问题时,也会导致redlock锁失效。

在混乱的分布式系统中,你不能假设系统时钟就是对的。

个人理解,相当于在业务层再做一层乐观锁。

一个好的分布式锁,无论 NPC 怎么发生,可以不在规定时间内给出结果,但并不会给出一个错误的结果。也就是只会影响到锁的「性能」(或称之为活性),而不会影响它的「正确性」。

1、Redlock 不伦不类 :它对于效率来讲,Redlock 比较重,没必要这么做,而对于正确性来说,Redlock 是不够安全的。

2、时钟假设不合理 :该算法对系统时钟做出了危险的假设(假设多个节点机器时钟都是一致的),如果不满足这些假设,锁就会失效。

3、无法保证正确性 :Redlock 不能提供类似 fencing token 的方案,所以解决不了正确性的问题。为了正确性,请使用有「共识系统」的软件,例如 Zookeeper。

好了,以上就是 Martin 反对使用 Redlock 的观点,看起来有理有据。

下面我们来看 Redis 作者 Antirez 是如何反驳的。

首先,Redis 作者一眼就看穿了对方提出的最为核心的问题: 时钟问题 。

Redis 作者表示,Redlock 并不需要完全一致的时钟,只需要大体一致就可以了,允许有「误差」。

例如要计时 5s,但实际可能记了 4.5s,之后又记了 5.5s,有一定误差,但只要不超过「误差范围」锁失效时间即可,这种对于时钟的精度的要求并不是很高,而且这也符合现实环境。

对于对方提到的「时钟修改」问题,Redis 作者反驳到:

Redis 作者继续论述,如果对方认为,发生网络延迟、进程 GC 是在客户端确认拿到了锁,去操作共享资源的途中发生了问题,导致锁失效,那这 不止是 Redlock 的问题,任何其它锁服务例如 Zookeeper,都有类似的问题,这不在讨论范畴内 。

这里我举个例子解释一下这个问题:

Redis 作者这里的结论就是:

所以,Redis 作者认为 Redlock 在保证时钟正确的基础上,是可以保证正确性的。

这个方案必须要求要操作的「共享资源服务器」有拒绝「旧 token」的能力。

例如,要操作 MySQL,从锁服务拿到一个递增数字的 token,然后客户端要带着这个 token 去改 MySQL 的某一行,这就需要利用 MySQL 的「事物隔离性」来做。

但如果操作的不是 MySQL 呢?例如向磁盘上写一个文件,或发起一个 HTTP 请求,那这个方案就无能为力了,这对要操作的资源服务器,提出了更高的要求。

也就是说,大部分要操作的资源服务器,都是没有这种互斥能力的。

再者,既然资源服务器都有了「互斥」能力,那还要分布式锁干什么?

利用 zookeeper 的同级节点的唯一性特性,在需要获取排他锁时,所有的客户端试图通过调用 create() 接口,在 /exclusive_lock 节点下创建临时子节点 /exclusive_lock/lock ,最终只有一个客户端能创建成功,那么此客户端就获得了分布式锁。同时,所有没有获取到锁的客户端可以在 /exclusive_lock 节点上注册一个子节点变更的 watcher 监听事件,以便重新争取获得锁。

锁释放依赖心跳。集群中占用锁的客户端失联时,锁能够被有效释放。一旦占用Znode锁的客户端与ZooKeeper集群服务器失去联系,这个临时Znode也将自动删除

zookeeper的高可用依赖zab。简单的说就是写入时,半数follower ack,写入成功。

zk是100%安全的么:

分析一个例子:

所以,得出一个结论: 一个分布式锁,在极端情况下,不一定是安全的。

redlock运维成本也比较高。单机有高可用问题。所以还是主从+哨兵这样的部署方式会好一些。

redis的缺点是:不是100%可靠。

mysql的缺点是:扛不住高流量请求。

可以二者结合,先用redis做分布式锁,扛住大部分流量缓解mysql压力。最后一定要用mysql做兜底保证100%的正确性。

关于redis分布式锁可能出现的问题和redis分布式锁存在的问题的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

标签列表