分布式锁选型 Redis vs Zookeeper

devtools/2024/11/15 6:02:05/

分布式锁作为分布式环境下并发控制利器,使用场景广泛。分布式锁通常可利用中间件RedisZookeeper来实现, 例如针对Java语言Redis有Redisson组件, Zk有Curator组件。

  • Redis是一款内存数据库,通常可用来做缓存,由于其执行命令使用单线程,也可以用来实现分布式锁,在集群模式下,Redis提供主从复制和哨兵机制实现高可用性;
  • Zookeeper是一款分布式协调中间件,集群模式下,Zk基于ZooKeeper Atomic Broadcast(ZAB)自实现了共识机制

从其中间件属性就可以看出Redis是偏AP,而Zk是偏CP的。

Distributed Locks with Redis

使用命令SET key value NX PX 30000对某个不存在的key赋值,如果设置成功则过期时间设为30000ms,由于Redis串行执行命令,且该命令的执行是原子的,可以达到分布式锁抢锁的效果,同时在释放锁时使用Lua脚本判断当前线程是否持有该锁,持有则释放锁:

if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])
elsereturn 0
end

使用Redis单机节点部署时存在单点故障,Redis官方推荐使用红锁算法(RedLock Algorithm)方式确保分布式锁的可用性:

红锁算法下,客户端获取锁的骤入如下:

  1. 获取当前初始时间戳
  2. 获取所有Redis的锁,获取锁的timeout可根据锁本身的过期时长来确定,比如锁10s过期,那么获取锁如果Redis 50ms没响应就算获取失败了,如果获取失败则忽略;
  3. 当全部获取完后,如果客户端获取到锁的数量超过Redis数量的半数 当前时间戳距离初始时间戳的时长小于锁的过期时长,则获取锁成功,成功后锁的有效时长即为之前的时间戳差值;
  4. 如果第3步判定获取锁失败,则把获取成功的锁给主动释放掉。

使用红锁算法仍然会存在如下问题:

比如有3台Redis server,分别为r1,r2,r3,客户端A获取到其中两台的锁(r1,r2),超过半数,获取锁成功,此时r2重启,内存数据丢失,客户端B又来获取同一把锁,此时可以获取到(r2,r3)的锁,超过半数,获取锁成功,这样客户端A和B就同时持有同一把锁。可以打开AOF,当机器重启后也会保留之前的数据,但是AOF的fsync策略默认为1s一次,如果Redis直接宕机,最多会丢失2s的数据,但如果fsync策略该为always也会影响写入性能。一种解决办法是宕机后不立即重启,而是等待一个过期时间后再重启,这样就不会有A和B同时持有同一把锁的情况发生。

除此之外,Martin Kleppman(DDIA作者)对RedLock算法提出反对,之后 Salvatore Sanfilippo 反对了反对。

Martin Kleppman提出RedLock会自动释放锁,当客户端发生类似GC等阻塞的耗时操作时无法保证锁的安全性,须添加fencing token(其实就是类似版本号的乐观锁)保证安全性。
而Salvatore Sanfilippo说Martin Kleppman的case是yy出来的,且分布式锁只是确保资源独占的一种高效的方式(just a weak lock to avoid, most of the times, concurrent accesses for performances reasons),并不应该依赖分布式锁完全确保资源独占。同时有评论指出:但是如果只是为了提升性能,而不是完全依赖分布式锁保证安全性,那其实单机Redis节点即可,没必要上RedLock。

Distributed Locks with Zookeeper

不同于RedLock,Zk集群内部自实现了共识算法,所以客户端直接与其中一台机器交互即可。

Zk基于ZAB协议实现共识机制,集群所有的写操作集中由Leader处理,Leader会广播消息到所有Follow,当ack超过半数时(包含自己的一票)会提交写并通知Follower提交。

Zk使用全局递增的zxid来记录消息进度,如果Leader宕机会在最新消息进度的节点中投票产生新Leader。

可以看到Zk一次写操作涉及多次交互,故写性能没有Redis高。

Conclusion

如果有其他方式兜底保证资源获取的互斥(如mysql更新时的version乐观锁),借助分布式锁是为了提升性能,用单机Redis即可;

如果完全依赖分布式锁保证资源获取的互斥(也不应该完全依赖,还是得加兜底),用Zookeeper。

References

  • Distributed Locks with Redis
  • How to do distributed locking
  • Is Redlock safe?
  • 深入了解Zookeeper核心原理

http://www.ppmy.cn/devtools/92042.html

相关文章

【漏洞复现】常见框架漏洞复现 合集

Web框架(Web framework)或者叫做Web应用框架(Web application framework),是用于进行Web开发的一套软件架构。大多数的Web框架提供了一套开发和部署网站的方式。为Web的行为提供了一套支持支持的方法。使用Web框架,很多的业务逻辑外的功能不需要自己再去…

electron调用c++ dll lib

主要的工具包 node-addon-apinode-gyp 主要的配置 {"variables": {# module_mac: "./../sdk/mac",},"targets": [{"target_name": "native_module","defines": ["NAPI_DISABLE_CPP_EXCEPTIONS"],&qu…

pygame小游戏

代码存在一些bug,感兴趣可自行修改,游戏运行后玩法与吃金币游戏类似。(代码及结果比较粗糙,仅供参考) 注:(图片、音乐、音效文件老是上传上传不上,想要可私,也可以自己找…

docker安装redis单机部署的redis.conf配置

下面是一个简单的 Redis 配置文件 (redis.conf) 示例,它适合docker单机部署环境,并且启用了密码保护。这个配置文件包含了最基本的设置,您可以根据需要进行扩展。 # 服务器监听的地址 bind 0.0.0.0# 服务器监听的端口 port 6379# 设置密码 r…

【mars3d】LayerType 的arcgis_feature类型的图层配置聚合属性,达到聚合效果

相关api文档: Global - V3.7.23 - Mars3D API文档 Global - V3.7.23 - Mars3D API文档 ArcGisWfsLayer - V3.7.23 - Mars3D API文档 实现代码: export function onMounted(mapInstance) {map mapInstance // 记录首次创建的map// 添加参考三维模型co…

五种IO模型、多路转接IO:select,poll,epoll(reactor)(技术

之前的系统部分的基础IO:就是冯诺依曼结果中的访问磁盘,用内存作为输入输出缓冲区提高效率 现在我们要说的高级IO(input/output):访问的外设(网络中就是网卡):我们的发送和接收接收其实大部分时…

Parsing error: The keyword ‘interface‘ is reserved配置优化

当我们在创建Vue项目时,Vue默认是配置了eslint代码规范检查的,不过我们在项目中Typescript来编写代码时,却报了标题的错误,对应的代码如下: <script lang="ts"> export default{name: "DlHeader" } </script><script setup lang=&quo…

Python pandas常见函数

Pandas库 基本概念读取数据数据处理数据输出其他常用功能 pip install pandas基本概念 数据结构 Series: 一维数据结构 import pandas as pd data pd.Series([10, 20, 30, 40], index[a, b, c, d]) print(data)DataFrame: 二维数据结构 data {Name: [Alice, Bob, Charlie],Ag…