为什么需要分布式锁?
在我们业务中,有些数据操作是不允许同时出现多次请求的。如添加一条订单,或者发起一笔支付,如果出现double-click事件,那么会出现脏数据,甚至重复支付的风险。我们必须要禁止对同一个订单进行多次下单和支付。那这个时候就需要对这种业务进行加锁。
同时,因为我们的应用很可能不只部署在一台机器上,那么我们就不能锁定一个单机资源,而是要锁住一个所有服务器都能访问到的资源,形成只能由单机持有资源的情况。单机A使用资源完成,需要释放资源,此时B才能继续申请试用资源。
Redis该用什么命令来支持分布式锁的场景?
分布式锁要求我们对资源获取的时候,如发现资源已被占用则返回一个错误状态。Redis里面的setNX即支持此命令。
A线程setnx key value
,返回1。
当A未释放锁的时候,B请求setnx key value
返回0,则B加锁失败。
上面的命令可能存在的问题
当A线程死循环或者A线程意外退出,导致无法释放锁,那么B及之后访问该资源的线程都无法再进行加锁操作,这样整个功能都无法使用,后果非常严重。所以我们需要对锁进行过期时间的控制。
解决办法
Redis可以用expire key seconds
的方式去使key过期。
但是还有个问题,两条指令的执行也可能在中间出现问题,导致expire执行失败。还有什么办法呢?此处可以用Redis的set指令。set key value EX seconds NX
上面在一条语句指令中在原来的基础上设置了过期时间。可以满足我们的需求。对value的要求: value需要是一个随机值。因为有可能出现以下情况。
1. A执行获取key命令成功。
2. A执行时间较长,key已过期,自动失效。
3. B申请加锁,执行命令成功。
4. A执行任务完成,删除B申请的锁。
所以,我们要在删除之前进行验证。所以删除时需要对随机设置的value进行验证,避免删除别的客户端申请的锁。
Redis分布式锁的弊端
Redis分布式锁的实现看起来貌似没问题了,但是随着业务量的增长和用户数量的增加,单台redis服务已经无法满足我们的需求。在分布式系统中,假设A & B & C都存储了部分锁信息,那么其中一台redis服务器的宕机和网络抖动都有可能导致加锁的失败。或者主从结构,主库还没来得及同步给从库,主服务宕掉了。那么这个锁就无法正确锁住相关业务,有可能造成脏数据的产生。
Red Lock
Red Lock是redis的作者antirez设计的一个非常复杂的分布式锁功能。这个功能还让分布式专家Martin和antirez进行了一系列的讨论,详见 antirez的文章。总之,RedLock的实现非常复杂,细节参看 Redis分布式锁 抽空再专门整理一下RedLock
其他分布式锁实现
Zookeeper!在《分布式锁ZK篇》我们再详细看一下ZK的分布式锁是如何使用的。
总结
Redis单机的分布式锁使用方式简单,但是集群不能保证高可用性。如果希望能保证分布式锁的高可用可以使用较为复杂的RedLock 或者 ZK。
觉得文章不错的话,欢迎转发收藏(请注明出处哦)。能够关注公众号就更好啦~
作者:BigBigBigPeach
地址:https://www.deepstack.top/articles/2019/07/18/1563462985488.html
微信公众号:deepstack