分布式锁的使用与注意事项

2021/01/15 Redis 分布式锁 共 1161 字,约 4 分钟

此文是2021年第一篇博客,计划每2周写一篇博客。

分布式锁的使用与注意事项

分布式锁介绍

最近项目需要用到分布式锁,在网上看了挺多写分布式锁的文章,于是打算写一篇关于分布式锁的文章。

单机的服务不需要用分布式锁,多线程抢夺同一资源,在内存中使用锁就可以了。

分布式锁适用于多台机器抢夺同一资源。多台机器抢夺同一资源一般有以下情况:

1.微服务系统中,同一个接口同样的参数可能会被上游连续调用2次,想要保护自身系统的一致性和幂等性,在接口入口处进行分布式锁的操作是很有必要的。

2.在一个服务中,一笔单据可能被不同的定时任务/接口捞起,会重复调用下游接口,使用分布式锁可以保护下游。

从上面列举的2种情况可以看出,分布式锁必须是独立的第三方。

目前最流行的分布式锁是Redis分布式锁,下面介绍一下Redis分布式锁的使用方法。

使用方法

Redis支持当key不存在时设置一个Key,因此把一个共享资源作为Key,就可以实现分布式锁。

set key value nx

其中nx表示当Key不存在时设置key,如果Key已经存在,不进行设置。

等程序运行结束,需要释放资源时,把锁删除就可以了:

del key

但这样直接使用分布式锁有以下几个问题:

1.如果获取锁的程序出现了异常,没有删除锁,那么后续这个资源就无法被其他程序使用了。因此锁需要设置过期时间。

2.锁有过期时间后出现了第二个问题。如果程序A执行的时间过长,导致锁失效;锁失效后程序B拿到锁,如果此时A释放了B的锁,那么C也可以拿到锁了。为了解决这个问题,需要在设置value的时候选择一个随机字符串,删除锁的时候需要先判断value是否为自己设置的value。

因此最终的用法如下:

set key random_string nx px 10000

其中px 10000表示该key的过期时间为10秒。

总结

上文提到的分布式锁的用法就是最常见的Redis分布式锁的用法,但也有缺点。因为分布式锁有过期时间,程序A始终有可能执行时间过长导致锁失效,比如JVM GC暂停了很久,程序A以为锁没失效,仍会进行资源的访问。

参考大佬文章:How to do distributed locking — Martin Kleppmann’s blog可以得出以下观点:

1.如果使用分布式锁是为了效率,防止不同客户端做重复的任务。即使锁失效了,也能保证幂等性,或者无需幂等性保证,那么单节点的Redis锁就够用了。

2.如果使用Redis锁是为了正确性,任何情况都不允许锁失效,那么分布式锁不适合这个场景,更推荐使用事务型数据库进行上锁操作,比如select…for update。

参考资料:

文档信息

Table of Contents