分布式锁介绍
对于平台系统,由于系统是集群模式,每个节点都是无状态模式,提供统一的功能,所以无法使用JVM的锁,需要使用分布式锁保证所有机器只有一个在执行
由于分布式锁一般是通过其他机器或进程(redis,zk,mysql等)来记录状态所以需要有的特性
- 并发获取锁时只能一个服务获取一个锁
- 业务服务挂掉后,锁最终也可以被释放
- 业务需要避免提前释放锁的问题
- 锁记录的服务节点挂掉后可以保证锁的一致性
- 锁释放后其他服务可以获取锁
- 锁释放时不会错误释放
分布式锁实现方式
redis实现
加锁
- 通过redis的命令jedis.set(key, value, nxxx, expx, time)来实现加锁(value为当前机器和线程的信息)
- redis的key为锁的信息,value为当前机器和线程的信息,如:主机IP+线程ID(172.16.1.1:100)
- redis的超时根据业务来订(如果有锁的续租,需要有统一的最小时间)
- 获取琐时不断的重试,知道超出等待时间
- 获取琐时判断value值是否和当前线程的一致,来实现可重入
解锁
- 通过lua脚本进行解锁
- lua脚本中应该包含两条指令,判断value值是否和当前线程的值一至,将锁删除
zk实现
加锁
- 在/xxxLock下创建临时的且有序的子节点
- 客户端获取/xxxLock下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听刚好在自己之前一位的子节点删除消息,获得子节点变更通知后重复此步骤直至获得锁
解锁
- 完成业务流程后,删除对应的子节点释放锁
对比
Redis为AP模式,可能存在获取到多把锁,比如在主从切换时,Zk为CP架构,不会存在获取到多把锁,因为在创建节点时会同步给半数节点。redis分布式锁相对更快
常见问题
redis锁的问题
获取锁后节点宕机
设置锁的超时时间,宕机后只会影响一定的时间
可重入
获取锁时判断设置的值是否和当前线程的信息一样
锁到期后还没执行完
使用watchDog进行续租
主从节点切换
- 强制同步从节点,会有性能问题
- 红锁解决(也会有性能问题)
ZK锁的问题
极端情况下,节点1获取到了锁,发送修改了值的请求,请求已经发送出去,但是还没有发送到资源服务器,此时节点1断开了ZK连接,节点2拿到了锁,也发送了修改值的命令,资源服务器先收到节点2的请求,之后又收到了节点1的请求就会出错