浅谈分布式锁( 二 )


浅谈分布式锁


上面/exclusive_lock/lock1节点,就可以被定义为一个锁 。
获取锁:
  1. 创建排他锁:需要获取排他锁的时候,所有的客户端都会试图调用create()接口,在/exclusive_lock节点下创建临时子节点/exclusive_lock/lock1 。zk/etcd会保证在所有的客户端中,最终只有一个客户端能够创建成功,那么就可以为该客户端获取锁 。
  2. watch排他锁:所有没有成功获取锁的客户端就要到/exclusive_lock节点上注册一个子节点变更的watcher监听,用于实时监听lock1节点的变更情况 。
释放锁即移除该临时节点 。由于/exclusive_lock/lock1是一个临时节点(etcd通过租约,通过发送心跳来续租),因此需要考虑以下情况:
  1. 已经获取锁的client发生了宕机,那么zk/etcd上整个临时节点就会被移除
  2. 正常完成业务之后,client会主动删除临时节点
无论在什么情况下移除lock节点,zk/etcd都会通知所有watcher /exclusive_lock的客户端 。这些客户端在接受到通知之后,再次重新发起分布式锁获取,即重复获取锁过程 。
浅谈分布式锁


共享锁(shared locks)
共享锁(读锁)表现如下:
进程p1对数据d1加上共享锁,那么当前事务只能对d1进行读取操作,其他事务也只能对整个数据加共享锁,直到该数据对象上的所有共享锁都被释放 。
与排他锁的区别是,加上排他锁之后,数据只对一个事务可见,而加上共享锁之后,数据对所有事务可见 。
定义锁:
同样是通过zk/etcd上的数据节点表示一个锁,格式可以参考/shared_lock/[hostname]-请求类型-序号 的临时节点 。
例如:下图中/shared_lock/host1-R-001就代表了一个共享锁
浅谈分布式锁


需要获取共享锁时,所有客户端都会到/shared_lock整个节点下创建一个临时顺序节点 。
  1. 如果当前是读请求,创建/shared_lock/192.168.0.1-R-01节点
  2. 如果是写请求,创建/shared_lock/192.168.0.1-W-02节点
根据共享锁的定义,
  1. 不同的事务可以对同一数据对象进行读取操作
  2. 更新操作必须在当前没有任何事务进行读写操作的情况下进行
zk/etcd的读写顺序:
  1. 创建完节点,获取/shared_lock节点下的所有子节点,并对该节点注册子节点变更的watcher监听
  2. 确定自己的节点序号,在所有子节点中的顺序
  3. 对于读请求:
    1. 如果没有比自己小的子节点,或者所有比自己小的子节点都是读请求,那么自己已经成功获取到共享锁,可以开始业务逻辑
    2. 如果比自己小的节点中有写请求,那么需要进入等待
  4. 对于写请求:
    1. 如果自己不是序号最小的子节点,那么就需要进入等待
  5. 接收到watcher通知后,重复步骤1
释放锁与排他锁是一致的:
浅谈分布式锁


例子:
浅谈分布式锁


  1. 主机10.16.0.1先进行读操作,完成后,将节点/shared_lock/10.16.0.1-R-000001删除
  2. 剩余4台机器均收到节点移除通知,然后重新从/shared_lock节点上获取一份新的子节点列表
  3. 每个主机判读自己的读写顺序 。接着,主机10.16.0.2检测到自己已经是需要最小的了,于是开始执行写操作,而其他主机发现没有轮到自己进行读取或者更新操作,于是继续等待
  4. 继续上面循环
  5. 推荐阅读