参考分布式锁用 Redis 还是 Zookeeper?
分布式锁当一个单点机器上有多个进程或者多个线程需要互斥访问一个共享资源时,就需要用到锁,这种情况下,只需要用各个编程语言库的锁即可,比如C++的pthread_mutex_lock,Java的synchronized和ReentrantLock等等来加锁。
但是本机的锁只能被本机的进程访问,而现实中的许多业务的访问量一般都比较大,单点机器无法支撑,必须多点部署,此时如果不同机器上的进程/线程仍然需要互斥访问一个全局的共享资源的话,就必须使用分布式锁,即为整个系统提供一个全局、唯一的获取锁的服务,然后每台机器节点在需要加锁时,都向这个服务请求一把锁,这样不同的机器节点拿到的就可以认为是同一把锁。
目前分布式锁可以用redis、zookeeper甚至数据库服务来实现。
Redis实现分布式锁一种非常常见的方案就是用redis实现分布式锁,思路非常简单:
- 需要加锁时,client在redis中设置一个key来表示加了锁,其中设置的value需要具备唯一性。
- 完成业务逻辑后,client删除这个key来表示释放这个锁,注意删除key时需要校验client的value跟加锁时设置的value是否相同,防止其他client误删这个key。
如果client想要获取锁时,发现锁已经被占用了,就会周期性地重复访问redis,直到锁被释放或者过时了,可见redis分布式锁这种轮询获取锁的方式会有较大的cpu开销。
RedLock算法用redis实现分布式锁还需要考虑redis的部署方式
- 单点部署:只要redis故障,立刻就无法提供分布式锁的服务了,所有请求分布式锁的机器都会报错影响业务。
- 哨兵模式:主从模式,主节点故障宕机时需要进行主从切换,此时也会有锁丢失的问题。
- 集群模式(cluster):有多个主节点,他们地位平等,存储的数据不重叠。(每个主节点对应有一些从节点,来保证主节点挂掉也能进行切换,从而正常提供服务)
RedLock算法基于cluster模式,有多个master节点,比如5个master节点的情况下,算法步骤为:
- 并发地在每个master节点上创建key(过期时间设置较短,一般就几十毫秒)
- 如果有超过一半的节点上创建key成功,比如5个节点就要求是3个节点(n / 2 +1),而且建立锁的时间小于超时时间,就算成功创建锁了
- 如果锁建立失败了,那么就依次删除这个锁
- 只要别人建立了一把分布式锁,你就得不断轮询去尝试获取锁
但RedLock算法也不一定能保证安全性,假设5个节点是A, B, C, D, E,客户端1在A, B, C上面拿到锁,D, E没有拿到锁,客户端1拿锁成功。 此时,C挂了重启,C上面锁的数据丢失(假设机器断电,数据还没来得及刷盘)。客户端2去取锁,从C, D, E 3个节点拿到锁,A, B没有拿到(还被客户端1持有),客户端2也超过多数派,也会拿到锁。
zookeeper实现分布式锁zookeeper的基础知识参考我的另一篇文章:zookeeper概念、原理与应用场景,其中,zookeeper实现分布式锁主要利用了znode的这三种特性:
- 有序节点:在zookeeper的目录下创建子节点时可以指定有序,那么生成的子节点就会自动添加整数序号,如果是第一个创建的子节点,那么生成的子节点为/lock/node-0000000000,下一个节点则为/lock/node-0000000001,依次类推。
- 临时节点:client可以选择创建临时节点,那么会话结束时zookeeper就会自动删除该临时节点。
- 事件监听:client读取数据时可以对节点设置事件监听,发生指定的事件时zookeeper会通知client,总共有四种事件:节点创建、节点删除、节点数据修改、子节点变更
借助这三种特性,分布式锁的实现方案为:
- 使用zookeeper的临时节点和有序节点,每个client获取锁就是在zk创建一个临时有序的节点,比如在/lock/目录下。
- 创建节点成功后,获取/lock目录下的所有临时节点,再判断当前client创建的节点是否是所有的节点中序号最小的节点
- 如果当前client创建的节点是所有节点序号最小的节点,则认为获取锁成功。
- 如果当前client创建的节点不是所有节点序号最小的节点,则对节点序号的前一个节点添加一个节点删除的事件监听,事件发生并且通知client后,再判断自己的节点是否最小节点,是则获取锁成功。
2、轮询访问redis获取锁的方式开销比较大。
2、锁被占用时只需设置监听器即可,不用一直轮询,开销较小。zk只有一个主节点负责写请求,高并发场景下有非常多的创建、删除 *** 作都需要主节点处理,压力会很大
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)