
分布式锁
分布式锁是分布式系统下的并发控制的机制,用于控制某个资源在同一时刻只能被同一应用所使用。一般来说最常见的两种实现分布式锁的方法就是redis锁和zookeeper
redis可以被多个客户端共享访问,用于保存分布式锁,redis的读写性能很高。其主要是通过set命令的NX参数实现。当key存在时会显示插入失败,表示加锁失败,如果不存在会显示插入成功,表示加锁成功。
redis的加锁需要包括读取锁变量,检查锁变量和设置锁变量,需要用原子操作方式完成。同时锁变量要设置过期时间,避免锁出现异常一直不释放,通过EX/PX参数设置。最后锁变量还要区分不同客户端的加锁操作,避免在释放锁的时候,出现误操作的情况,需要给每个客户设置唯一值,标识客户端。最终的分布式命令如下:
SET lock_key unique_value NX PX 10000
其中lock_key是key键,unique_value是客户端生成的唯一标识,用于标识客户端,10000是过期时间10s。
redis锁解锁就是把lock_key删除,需要先检测一下是否是对应的客户端,这是两个操作,需要用lua脚本来保证解锁的原子性。lua脚本能保障原子性的原因是redis是单线程执行的,执行过程中是不会被其他客户端请求所影中断的,只有lua脚本结束后,其他被放入队列中的客户端请求指令才会依次执行。lua执行失败后是不会回滚的,但会导致后面的脚本也不会执行。
zookeeper利用临时顺序节点和监听机制来实现分布式锁。监视器watcher创建节点时,可以注册一个该节点的监视器,当节点状态发生改变之后,就会触发watch机制,zookeeper会向客户端发送且仅发送一次通知,watch只会触发一次。
zookeeper首先会有一个持久节点,然后请求节点进来后会创建临时有序节点,然后节点会判断是不是持久节点下序号最小的节点,如果是的话会获得一个锁,否则会阻塞这个节点线程,设置一个监听器监听前面的节点,当获得锁之后就可以处理业务逻辑,然后删除节点,后一个节点通过watcher唤醒线程。
zookeeper实现的分布式锁是强一致性的,因为ZAB协议是基于CP的,但是会导致性能的下降。zookeeper锁比redis锁健壮性更加强,因为redis锁主节点宕机后会丢失锁,网络分区概率低可以使用ZK锁,高并发场景可以使用redis锁。
分布式锁是分布式系统下的并发控制的机制,用于控制某个资源在同一时刻只能被同一应用所使用。一般来说最常见的两种实现分布式锁的方法就是redis锁和zookeeper
redis可以被多个客户端共享访问,用于保存分布式锁,redis的读写性能很高。其主要是通过set命令的NX参数实现。当key存在时会显示插入失败,表示加锁失败,如果不存在会显示插入成功,表示加锁成功。
redis的加锁需要包括读取锁变量,检查锁变量和设置锁变量,需要用原子操作方式完成。同时锁变量要设置过期时间,避免锁出现异常一直不释放,通过EX/PX参数设置。最后锁变量还要区分不同客户端的加锁操作,避免在释放锁的时候,出现误操作的情况,需要给每个客户设置唯一值,标识客户端。最终的分布式命令如下:
SET lock_key unique_value NX PX 10000
其中lock_key是key键,unique_value是客户端生成的唯一标识,用于标识客户端,10000是过期时间10s。
redis锁解锁就是把lock_key删除,需要先检测一下是否是对应的客户端,这是两个操作,需要用lua脚本来保证解锁的原子性。lua脚本能保障原子性的原因是redis是单线程执行的,执行过程中是不会被其他客户端请求所影中断的,只有lua脚本结束后,其他被放入队列中的客户端请求指令才会依次执行。lua执行失败后是不会回滚的,但会导致后面的脚本也不会执行。
zookeeper利用临时顺序节点和监听机制来实现分布式锁。监视器watcher创建节点时,可以注册一个该节点的监视器,当节点状态发生改变之后,就会触发watch机制,zookeeper会向客户端发送且仅发送一次通知,watch只会触发一次。
zookeeper首先会有一个持久节点,然后请求节点进来后会创建临时有序节点,然后节点会判断是不是持久节点下序号最小的节点,如果是的话会获得一个锁,否则会阻塞这个节点线程,设置一个监听器监听前面的节点,当获得锁之后就可以处理业务逻辑,然后删除节点,后一个节点通过watcher唤醒线程。
zookeeper实现的分布式锁是强一致性的,因为ZAB协议是基于CP的,但是会导致性能的下降。zookeeper锁比redis锁健壮性更加强,因为redis锁主节点宕机后会丢失锁,网络分区概率低可以使用ZK锁,高并发场景可以使用redis锁。


