什么是分布式锁?
对于一个单机的系统,我们可以通过synchronized或者ReentrantLock等这些常规的加锁方式来实现,然而对于一个分布式集群的系统而言,单纯的本地锁已经无法解决问题,所以就需要用到分布式锁了,通常我们都会引入三方组件或者服务来解决这个问题,比如数据库、Redis、Zookeeper等。
分布式锁
1、什么是分布式锁:
分布式锁,即分布式系统中的锁。在单体应用中我们通过锁解决的是控制共享资源访问的问题,而分布式锁,就是解决了分布式系统中控制共享资源访问的问题。与单体应用不同的是,分布式系统中竞争共享资源的最小粒度从线程升级成了进程。
在 redis
中提供了一个命令:setnx(SET if not exists)
由于 redis
的单线程的,用了命令之后,只能有一个客户端对某一个 key
设置值,在没有过期或删除key
的时候是其他客户端是不能设置这个 key
的
redis
命令说明:
-
setnx
命令(set if not exists),当且仅当key
不存在时,将key
的值设为value
。若给定的key
已经存在,则SETNX
不做任何动作。返回1,说明该进程获得锁,将
key
的值设为value
返回0,说明其他进程已经获得了锁,进程不能进入临界区。命令格式:
setnx [key] [value]
-
get
命令:获取key
的值,如果存在,则返回;如果不存在,则返回nil
命令格式:
get lock.key
-
getset
命令:该方法是原子的,对key
设置newValue
这个值,并且返回key
原来的旧值。命令格式:
getset [key] [value]
-
del
命令:删除redis
中指定的key
命令格式:
del [key]
如何控制Redis实现分布式锁有效时长呢?
锁续期:(这种机制类似于redisson的看门狗机制,文章后面会详细说明)
我们可以让获得锁的线程开启一个守护线程,用来给快要过期的锁“续期”。
① 假设线程A执行了29 秒后还没执行完,这时候守护线程会执行 expire 指令,为这把锁续期 20 秒。守护线程从第 29 秒开始执行,每 20 秒执行一次。
② 情况一:当线程A执行完任务,会显式关掉守护线程。
③ 情况二:如果服务器忽然断电,由于线程 A 和守护线程在同一个进程,守护线程也会停下。这把锁到了超时的时候,没人给它续命,也就自动释放了。
redisson实现的分布式锁能解决主从一致性的问题吗
回答:这个是不能的,比如,当线程1加锁成功后,master节点数据会异步复制到slave节点,此时当前持有Redis锁的master节点宕机,slave节点被提升为新的master节点,假如现在来了一个线程2,再次加锁,会在新的master节点上加锁成功,这个时候就会出现两个节点同时持有一把锁的问题。
我们可以利用redisson提供的红锁来解决这个问题,它的主要作用是,不能只在一个redis实例上创建锁,应该是在多个redis实例上创建锁,并且要求在大多数redis节点上都成功创建锁,红锁中要求是redis的节点数量要过半。这样就能避免线程1加锁成功后master节点宕机导致线程2成功加锁到新的master节点上的问题了。
但是,如果使用了红锁,因为需要同时在多个节点上都添加锁,性能就变的很低了,并且运维维护成本也非常高,所以,我们一般在项目中也不会直接使用红锁,并且官方也暂时废弃了这个红锁
问题:好的,如果业务非要保证数据的强一致性,这个该怎么解决呢?
回答: redis本身就是支持高可用的,做到强一致性,就非常影响性能,所以,如果有强一致性要求高的业务,建议使用zookeeper实现的分布式锁,它是可以保证强一致性的。