此文是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。
参考资料:
文档信息
- 本文作者:last2win
- 本文链接:https://last2win.com/2021/01/15/redis-lock/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)