一般实现分布式锁都有哪些方式?使用 Redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?

目录

1.Redis 分布式锁

(1)Redis 最普通的分布式锁

 (2)RedLock 算法

2.zk 分布式锁

3.redis 分布式锁和zk分布式锁的对比


1.Redis 分布式锁

        官方叫做 RedLock 算法,是 Redis 官方支持的分布式锁算法。
这个分布式锁有3个重要的考量点:
        · 互斥(只能有一个客户端获取锁)
        · 不能死锁
        · 容错(只要大部分 Redis 节点创建了这把锁就可以)

(1)Redis 最普通的分布式锁

        第一个最普通的实现方式,就是在 Redis 里使用 SET key value [EX seconds][PX milliseconds] NX 创建一个 key,这样就算加锁。其中:
        · NX :表示只有 key 不存在的时候才会设置成功,如果此时 redis 中存在这个 key ,那么设置失败,返回 nil 。
        · EX seconds :设置 key 的过期时间,精确到秒级。意思是 seconds 秒后锁自动释放,别人创建的时候如果发现已经有了就不能加锁了。
        · PX milliseconds :同样是设置 key 的过期时间,精确到毫秒级。
        比如执行以下命令:

SET resource_name my_random_value PX 30000 NX

        释放锁就是删除 key ,但是一般可以用 lua 脚本删除,判断 value 一样才删除: 

-- 删除锁的时候,找到 key 对应的 value,跟自己传过去的 value 做比较,如果是一样的才删除
if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])
elsereturn 0
end

        为啥要用 random_value 随机值呢?因为如果某个客户端获取到了锁,但是阻塞了很长时间才执行完,比如说超过了 30s,此时可能已经自动释放锁了,此时可能别的客户端已经获取到了这个锁,要是你这个时候直接删除 key 的话会有问题,所以得用随机值加上面的 lua 脚本来释放锁。
        但是这样是肯定不行的。因为如果是普通的 Redis 单实例,那就是单点故障。或者是 Redis 普通主从,那 Redis 主从异步复制,如果主节点挂了(key 就没有了),key 还没同步到从节点,此时从节点切换为主节点,别人就可以 set key,从而拿到锁。 

 (2)RedLock 算法

        这个场景是假设有一个 Redis cluster,有5个 Redis master 实例。然后执行如下步骤获取一把锁:
        1.获取当前时间戳,单位是毫秒;
        2.跟上面类似,轮流尝试在每个 master 节点上创建锁,过期时间较短,一般就几十毫秒;

        3.尝试在大多数节点上建立一个锁,比如5个节点就要求是3个节点 n/2+1;

        4.客户端计算建立好锁的时间,如果建立锁的时间小于超时时间,就算建立成功了;

        5.要是锁建立失败了,那么就依次之前建立过的锁删除;
        6.只要别人建立了一把分布式锁,你就得不断轮询去尝试获取锁。

Redis 官方 给出了以上两种基于 Redis 实现分布式锁的方法,详细说明可以查看:
https://redis.io/topics/distlock

2.zk 分布式锁

        zk 分布式锁,其实可以做的比较简单,就是某个节点尝试创建临时 znode,此时创建成功了就获取了这个锁:这个时候别的客户端来创建锁会失败,只能注册个监听器监听这个锁。释放锁就是删除这个 znode,一旦释放掉就会通知客户端,然后有一个等待着的客户端就可以再次重新加锁。

 

/**
* ZooKeeperSession
*/
public class ZooKeeperSession {private static CountDownLatch connectedSemaphore = new CountDownLatch();private ZooKeeper zookeeper;private CountDownLatch latch;public ZooKeeperSession() {try {this.zookeeper = new ZooKeeper("192.168.31.187:2181,192.168.31try {connectedSemaphore.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("ZooKeeper session established......");} catch (Exception e) {e.printStackTrace();}
}/**
* 获取分布式锁
*
* @param productId
*/
public Boolean acquireDistributedLock(Long productId) {String path = "/product-lock-" + productId;try {zookeeper.create(path, "".getBytes(), Ids.OPEN_ACL_UNSAFE, Crereturn true;} catch (Exception e) {while (true) {try {// 相当于是给node注册一个监听器,去看看这个监听器是否存在Stat stat = zk.exists(path, true);if (stat != null) {this.latch = new CountDownLatch(1);this.latch.await(waitTime, TimeUnit.MILLISECONDS);this.latch = null;}zookeeper.create(path, "".getBytes(), Ids.OPEN_ACL_UNSAreturn true;} catch (Exception ee) {continue;}}
}return true;}
}/**
* 释放掉一个分布式锁
*
* @param productId
*/
public void releaseDistributedLock(Long productId) {String path = "/product-lock-" + productId;try {zookeeper.delete(path, -1);System.out.println("release the lock for product[id=" + produc} catch (Exception e) {e.printStackTrace();}
}/**
* 建立 zk session 的 watcher
*/
private class ZooKeeperWatcher implements Watcher {public void process(WatchedEvent event) {System.out.println("Receive watched event: " + event.getState(if (KeeperState.SyncConnected == event.getState()) {connectedSemaphore.countDown();}if (this.latch != null) {this.latch.countDown();}}
}/**
* 封装单例的静态内部类
*/
private static class Singleton {private static ZooKeeperSession instance;static {instance = new ZooKeeperSession();}public static ZooKeeperSession getInstance() {return instance;}
}/**
* 获取单例
*
* @return
*/
public static ZooKeeperSession getInstance() {return Singleton.getInstance();
}/**
* 初始化单例的便捷方法
*/
public static void init() {getInstance();
}
}

        也可以采用另一种方式,创建临时顺序节点:如果有一把锁,被多个人给竞争,此时多个人会排队,第一个拿到锁的人会执行,然后释放锁;后面的每个人都会去监听排在自己前面的那个人创建的 node ,一旦某个人释放了锁,排在自己后面的人就会被 ZooKeeper 给通知,一旦被通知了之后,就 ok了,自己就获取到了锁,就可以执行代码了。

public class ZooKeeperDistributedLock implements Watcher {private ZooKeeper zk;private String locksRoot = "/locks";private String productId;private String waitNode;private String lockNode;private CountDownLatch latch;private CountDownLatch connectedLatch = new CountDownLatch(1);private int sessionTimeout = 30000;public ZooKeeperDistributedLock(String productId) {this.productId = productId;try {String address = "192.168.31.187:2181,192.168.31.19:2181,192.1zk = new ZooKeeper(address, sessionTimeout, this);connectedLatch.await();} catch (IOException e) {throw new LockException(e);} catch (KeeperException e) {throw new LockException(e);} catch (InterruptedException e) {throw new LockException(e);}}public void process(WatchedEvent event) {if (event.getState() == KeeperState.SyncConnected) {connectedLatch.countDown();return;}if (this.latch != null) {this.latch.countDown();}}public void acquireDistributedLock() {try {if (this.tryLock()) {return;} else {waitForLock(waitNode, sessionTimeout);}} catch (KeeperException e) {throw new LockException(e);} catch (InterruptedException e) {throw new LockException(e);}}public boolean tryLock() {try {// 传入进去的locksRoot + “/” + productId// 假设productId代表了一个商品id,比如说1// locksRoot = locks// /locks/10000000000,/locks/10000000001,/locks/10000000002lockNode = zk.create(locksRoot + "/" + productId, new byte[0],// 看看刚创建的节点是不是最小的节点// locks:10000000000,10000000001,10000000002List<String> locks = zk.getChildren(locksRoot, false);Collections.sort(locks);if(lockNode.equals(locksRoot+"/"+ locks.get(0))){//如果是最小的节点,则表示取得锁return true;}//如果不是最小的节点,找到比自己小1的节点int previousLockIndex = -1;for(int i = 0; i < locks.size(); i++) {if(lockNode.equals(locksRoot + “/” + locks.get(i))) {previousLockIndex = i - 1;break;}}this.waitNode = locks.get(previousLockIndex);} catch (KeeperException e) {throw new LockException(e);} catch (InterruptedException e) {throw new LockException(e);}return false;}private boolean waitForLock(String waitNode, long waitTime) throws IntStat stat = zk.exists(locksRoot + "/" + waitNode, true);if (stat != null) {this.latch = new CountDownLatch(1);this.latch.await(waitTime, TimeUnit.MILLISECONDS);this.latch = null;}return true;}public void unlock() {try {// 删除/locks/10000000000节点// 删除/locks/10000000001节点System.out.println("unlock " + lockNode);zk.delete(lockNode, -1);lockNode = null;zk.close();} catch (InterruptedException e) {e.printStackTrace();} catch (KeeperException e) {e.printStackTrace();}}public class LockException extends RuntimeException {private static final long serialVersionUID = 1L;public LockException(String e) {super(e);}public LockException(Exception e) {super(e);}}
}

3.redis 分布式锁和zk分布式锁的对比

        redis 分布式锁,需要自己不断去尝试获取锁,比较消耗性能。
        zk 分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销小。
        另外,如果是 Redis 获取锁的那个客户端出现 bug 挂了,那么只能等待超时时间才能释放锁;而zk 的话,因为创建的是临时 znode,只要客户端挂了,znode 就没了,此时自动释放锁。
        Redis 分布式锁大家没发现好麻烦吗?遍历上锁,计算时间等等;zk的分布式锁语义清晰实现简单。
        所以先不分析太多的东西,就说这两点,我个人实践认为 zk 的分布式锁比 Redis 的分布式锁牢靠、而且模型简单易用。

( ps:一个点赞一份爱,点个关注不迷路!)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3014830.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

24年最新AI数字人简单混剪

24年最新AI数字人简单混剪 网盘自动获取 链接&#xff1a;https://pan.baidu.com/s/1lpzKPim76qettahxvxtjaQ?pwd0b8x 提取码&#xff1a;0b8x

Web 性能终极指南(点击即可跳转对应的内容界面,制作不易。给个关注谢谢。)

Web 性能&#x1f680;终极指南 #[HTML全文] #CSS的 #JavaScript的 #反应 点击即可跳转对应的内容界面&#xff0c;制作不易。给个关注谢谢。 有很多方法可以加快您的网站速度。难道你不希望每个网络性能提示都集中在一个地方吗&#xff1f;我也是这么想的&#xff0c;所以我把…

揭秘数据可视化:五款利器助力决策

在当今这个数据驱动的时代&#xff0c;数据可视化已成为企业决策、数据分析不可或缺的一部分。通过直观、生动的图形、图像&#xff0c;数据可视化能够更快速、更准确地传达信息&#xff0c;帮助企业洞察数据背后的价值。本文将为您介绍几款优秀的数据可视化工具。 一、山海鲸…

python 使用 MQTT

目录结构 1、py代码 offRelay12-yixing.py # _*_ coding: utf-8 _*_ # 须用到第三方库&#xff1a;paho-mqtt # 安装命令 python3 -m pip install paho-mqttimport time import json import paho.mqtt.client as mqtt# 函数&#xff1a;关闭所有房间的12路继电器模块上指定的…

原型模式类图与代码

现要求实现一个能够自动生成求职简历的程序&#xff0c;简历的基本内容包括求职者的姓名、性别、年龄及工作经历。希望每份简历中的工作经历有所不同&#xff0c;并尽量减少程序中的重复代码。 采用原型模式(Prototype)来实现上述要求&#xff0c;得到如图 7.25 所示的类图。 原…

王春城:精益变革对于组织的长期发展有什么好处?

众所周知&#xff0c;精益变革不仅仅是一种管理方法的改进&#xff0c;更是一种思维方式的转变&#xff0c;旨在消除浪费、提高效率和持续改进。那么&#xff0c;精益变革对于组织的长期发展究竟有什么好处呢&#xff1f;下面天行健王春城老师将从多个方面详细阐述。 首先&…

【承装修、试电力施工许可证申报指南】广西承装(修、试)电力设施许可证资质代办流程

【承装修、试电力施工许可证申报指南】广西承装(修、试)电力设施许可证资质代办流程 广西承装(修、试)电力设施许可证资质代办流程 在广西地区&#xff0c;承装、修、试电力设施许可证的资质代办流程相对复杂&#xff0c;但遵循一定的步骤和规定&#xff0c;可以确保流程的顺利…

Vue开发中Element UI/Plus使用指南:常见问题(如Missing required prop: “value“)及中文全局组件配置解决方案

文章目录 一、vue中使用el-table的typeindex有时不显示序号Table 表格显示索引自定义索引报错信息解决方案 二、vue中Missing required prop: “value” 报错报错原因解决方案 三、el-table的索引值index在翻页的时候可以连续显示方法一方法二 四、vue3中Element Plus全局组件配…

男士内裤品牌哪个好?口碑最好的男士内裤汇总

许多男士选内裤可能比较随意&#xff0c;但实际上作为长时间贴合身体皮肤的贴身衣物&#xff0c;经常会出很多汗。而普通的内裤会吸附很多汗水却不易干&#xff0c;导致细菌含量超标&#xff0c;摩擦力增强&#xff0c;容易造成破皮感染从而影响健康。 但是现在的男士内裤种类和…

充电宝哪个牌子好?比较好用充电宝牌子,这些品牌别错过

作为一个资深的手机控&#xff0c;深知手机对于现代人的重要性。从早到晚&#xff0c;无论是点外卖、看剧还是处理各种事务&#xff0c;手机几乎成了我生活的必需品。然而&#xff0c;手机电量的问题总是让人头疼。在家时&#xff0c;找个插座充电自然不成问题&#xff0c;但出…

vue2后台管理项目

一:项目准备 1)拉取模板代码 远程仓库复制到本地仓库. 2)安装后的项目 路径 code 文件夹 会打开vscode的文件夹. 3)安装vetur和eslint插件可以保存时自动修改不规范的地方. 4)App内有一级路由,路由组件导入如果是layout架子,会导入的是文件夹下的index.js没有则导入index.v…

Baidu Comate:你的智能编码助手,编程效率倍增的秘密武器

Baidu Comate智能编码助手 Baidu Comate 智能编码助手简单介绍安装使用查看Comate插件功能智能代码提示使用飞浆和百度智能小程序进行智能问答使用AutoWork插件实现二次函数图像的生成引用Comate知识库存在的问题结束语 Baidu Comate 智能编码助手简单介绍 Baidu Comate&#x…

rmallox勒索病毒肆虐,如何保护网络安全?

rmallox勒索病毒与网络安全的关系可以从以下几个方面来阐述&#xff1a; 一、rmallox勒索病毒的特性 rmallox勒索病毒是一种极具破坏性的计算机病毒&#xff0c;它具有多个显著特性&#xff0c;这些特性使得该病毒对网络安全构成了严重威胁。具体来说&#xff0c;rmallox病毒具…

leetcode-缺失的第一个正整数-96

题目要求 思路 1.这里的题目要求刚好符合map和unordered_map 2.创建一个对应map把元素添加进去&#xff0c;用map.find(res)进行查找&#xff0c;如果存在返回指向该元素的迭代器&#xff0c;否则返回map::end()。 代码实现 class Solution { public:int minNumberDisappeare…

C语言例题37、输入三组数字,按照从小到大的顺序排列输出

#include<stdio.h>int main() {int num[3];printf("请输入3组数字&#xff1a;");for (int i 0; i < 3; i)scanf("%d", &num[i]);for (int i 0; i < 2; i) { //每次选出最小值放在数组前面for (int j i 1; j < 3; j) {if (num[j] …

移动硬盘无法被识别怎么办?恢复移动硬盘3个正确做法

移动硬盘已成为我们日常生活和工作中不可或缺的数据存储设备。然而当移动硬盘突然无法被电脑识别时&#xff0c;往往会让人倍感焦虑。面对这种情况我们不必过于慌张&#xff0c;下面一起来看看指南解决。 解决方法一&#xff1a;检查硬件连接与供电 检查接口连接&#xff1a…

使用免费的数据恢复软件通过简单的步骤恢复丢失的数据

犯错是人之常情&#xff01;您有时可能会意外地从PC或笔记本电脑中删除重要数据&#xff0c;旧的家庭或大学视频/照片&#xff0c;如果您面临数据丢失&#xff0c;则可以使用数据恢复软件轻松恢复丢失的数据。 奇客数据恢复软件可让您从笔记本电脑&#xff0c;PC和可移动存储设…

pytest(二):关于pytest自动化脚本编写中,初始化方式setup_class与fixture的对比

一、自动化脚本实例对比 下面是一条用例,使用pytest框架,放在一个类中,两种实现方式: 1.1 setup_class初始化方式 1. 优点: 代码结构清晰,setup_class 和 teardown_class 看起来像传统的类级别的 setup 和 teardown 方法。2. 缺点: 使用 autouse=True 的 fixture 作为…

VBA在Excel中注册登录界面的应用

Excel工作表也可以做一个小程序,登录注册后可以访问或修改。为了简便,没有做复杂的控件,能说明问题就行。可以根据需要添加更多的判断条件,控制注册和访问人数。本次操作对注册没有任何限制,只要注册后就可以根据注册的账号和密码进行访问和修改。注册登录界面截图: 操作…

吴恩达2022机器学习专项课程C2(高级学习算法)W1(神经网络):2.4 神经网络层

目录 神经网络第一层&#xff08;隐藏层&#xff09;计算过程1.输入向量2.神经元的计算2.标识不同神经元3.层输出&#xff08;激活&#xff09;向量4.神经网络分层5.标识不同层 神经网络第二层&#xff08;输出层&#xff09;计算过程1.输入向量2.层输出&#xff08;激活&#…