首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Redis实现分布式锁相关注意事项

Redis实现分布式锁相关注意事项

查看了不少关于redis实现分布式锁的文章,无疑要设计一个靠谱的分布式并不太容易,总会出现各种鬼畜的问题;现在就来小述一下,在设计一个分布式锁的过程中,会遇到一些什么问题

I. 背景知识

借助redis来实现分布式锁(我们先考虑单机redis的模式),首先有必要了解下以下几点:

单线程模式

setnx : 当不存在时,设置value,并返回1; 否则返回0

getset : 设置并获取原来的值

expire : 设置失效时间

get : 获取对应的值

del : 删除

ttl : 获取key对应的剩余时间,若key没有设置过超时时间,或者压根没有这个key则返回负数(可能是-1,-2)

watch/unwatch : 事务相关

II. 方案设计

1. 设计思路

获取锁:

调用 setnx 尝试获取锁,如果设置成功,表示获取到了锁

设置失败,此时需要判断锁是否过期

未过期,则表示获取失败;循环等待,并再次尝试获取锁

已过期,getset再次设置锁,判断是否获取了锁(根据返回的值进行判断,后面给出具体的方案)

若失败,则重新进入获取锁的逻辑

释放锁:

一个原则就是确保每个业务方释放的是自己的锁

2. getset的实现方案

网上一种常见的case,主要思路如下

setnx 尝试获取锁

失败,则 get 获取锁的value (一般是 uuid_timstamp)

判断是否过期,若没有过期,则表示真的获取失败

若过期,则采用 getset设置,尝试获取锁

实现代码如下

观察获取锁的逻辑,特别是获取超时锁的逻辑,很容易想到有一个问题getSet方法会不会导致写数据混乱的问题,简单来说就是多个线程同时判断锁超时时,执行getSet设置锁时,最终获取锁的线程,能否保证和redis中的锁的value相同

上面的实现方式,一个混乱的case如下:

三个线程a,b,c 都进入到了锁超时的阶段

线程a, 获取原始值 oldVal, 并设置 t1

线程b, 获取线程a设置的 t1, 并重设为 t2

线程c, 获取线程b设置的 t2, 并重设为 t3

线程a,判断,并正式获取到锁

线程b,判断失败,恢复原来锁的内容为t1

线程c, 判断失败,恢复原来锁的内容为t2

问题出现了,获取锁的线程a,期望所得内容为t1, 但是实际为t2; 导致无法释放锁

实际验证

在上面的代码中,配合测试case,加上一些日志输出

测试case

部分输出结果:

如何解决上面这个问题呢?

上面是典型的并发导致的问题,当然可以考虑从解决并发问题的角度出发来考虑,一个常见的方式就是加锁了,思路如下:(不详细展开了)

在判断超时之后,加锁

再次获取对应的值,判断是否超时,是则执行上面的操作

否则退出逻辑,继续循环

这种实现方式,会有以下的问题:

getset 这个方法执行,可能导致写入脏数据

基于服务器时钟进行超时判断,要求所有服务器始终一致,否则有坑

3. expire实现方式

相比于前面一种直接将value设置为时间戳,然后来比对的方法,这里则直接借助redis本身的expire方式来实现超时设置,主要实现逻辑相差无几

获取锁的逻辑相比之前的,就简单很多了,接下来则需要简单的分析下,上面这种实现方式,会不会有坑呢?我们主要看一下获取锁失败的场景

如果获取锁失败

表示有其他的业务方已经获取到了锁

此时,只能等持有锁的业务方主动释放锁

判断锁是否设置了超时时间,若没有则加一个(防止设置超时时间失败导致问题)

从上面这个逻辑来看问题不大,但是有个问题,case :

如某个业务方setnx获取到了锁,但是因为网络问题,过了很久才获取到返回,此时锁已经失效并被其他业务方获取到了,就会出现多个业务方同时持有锁的场景

III. 小结说明

想基于redis实现一个相对靠谱的分布式锁,需要考虑的东西还是比较多的,而且这种锁并不太适用于业务要求特别严格的地方,如

一个线程持有锁时,如果发生gc,导致锁超时失效,但是自己又不知道,此时就会出现多个业务方同时持有锁的场景

对于锁超时的场景,需要仔细考虑,是否会出现并发问题

确保只能释放自己的锁(以防止释放了别人的锁,出现问题)

V. 其他

声明

尽信书则不如,已上内容,纯属一家之言,因本人能力一般,见解不全,如有问题,欢迎批评指正

扫描关注,java分享

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180121A0CM0B00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券