莱芜房产网站/河南今日头条最新消息

莱芜房产网站,河南今日头条最新消息,单个页面的网站,昆明微信网站建设写在前面 本人在学习Redis过程中学习到分布式锁时太多困惑和疑难杂点 需要总结梳理思路 以下思路都是最简单最基本的思路 主要用到Redission工具类 会涉及到看门狗机制等 本文内容部分引自Javaguide,小林coding等热门八股 用于个人学习用途 分布式锁介绍 对于单机多线程来说…

写在前面

本人在学习Redis过程中学习到分布式锁时太多困惑和疑难杂点 需要总结梳理思路 以下思路都是最简单最基本的思路 主要用到Redission工具类 会涉及到看门狗机制等 本文内容部分引自Javaguide,小林coding等热门八股 用于个人学习用途

分布式锁介绍

对于单机多线程来说 通常用ReetrantLock类 synchronized关键字这类JDK自带的本地锁来控制一个JVM进程内多线程对本地共享资源的访问

图解引自JavaGuide:

 线程访问共享资源是 互斥 的!也就是同一时刻只有单线程可获取本地锁访问共享资源

在分布式系统下 不同服务或者客户端通常运行在多个独立的JVM进程若多个JVM进程共享同份资源 使用本地锁就没办法实现资源的互斥访问了 为了解决这种问题 分布式锁 诞生了

举例子:下单服务一共部署 3 份 都对外提供服务 用户下订单之前后台要查库存 为了防止超卖 就需要加锁来实现对检查库存操作的同步访问 由于订单服务位于不同JVM进程中 本地锁在这种情况下就无法正常工作  因此要用到分布式锁 才能解决多线程不在同个JVM进程下也能获取同把锁 劲儿实现共享资源的互斥访问

图解引自JavaGuide:

可以看出来两者区别就在于 分布式锁就是联系起不同的JVM进程并保证所有JVM都可以对共享资源进行同步操作 基本的要求还是大体一致的 同样 独立进程里的线程访问共享资源是互斥的 某一时刻只有单线程可获取到分布式锁访问共享资源

分布式锁最基本要满足:

1.互斥:任何一个时刻 锁只能被一个线程持有(保证原子操作以免造成数据不一致问题)

2.高可用:锁的服务是高可用的 即使客户端释放锁的代码逻辑出现问题 锁最后还是会被释放 不影响其他线程中进程对共享资源的访问(一定可以释放锁 无论是异常还是出错 锁的效率很高)

3.可重入:一个节点获取了锁以后还可以再次获取锁(多次使用客户端一个功能 增删改)

实现分布式锁:

Redis 或 ZooKeeper实现分布式锁 以Redis为例

how to 实现?首先 从分布式锁的定义出发 无论是本地锁还是分布式锁 都有共性--“互斥”

在Redis里 setnx命令可以实现互斥 setnx(set if Not eXist)对应Java里的setIfAbsent方法 如果key不存在 才会设置key的值 若key存在就什么都不做

> SETNX lockKey uniqueValue
(integer) 1
> SETNX lockKey uniqueValue
(integer) 0

释放锁 用del删除key就行

> DEL lockKey
(integer) 1

但是这里有很多可能 在自学过程中 想到过若在当前线程已经获取锁的情况下

如果使用多个命令(如 setnx 和 expire设置过期时间),可能会出现:

1.成功获取锁 但是设置过期时间失败 导致锁无法正常释放

2.释放锁时 误删其他线程的锁(如锁已过期,但当前线程误删了新线程的锁)

为了解决特殊情况 需要保证数据前后操作一致性 也就是原子性 最常见的就是用lua脚本通过key对应的value(唯一值)来判断

lua脚本基于c语言 保证了解锁操作的原子性 因为Redis在执行lua脚本时可以用原子性的方式执行 从而保证锁释放操作的原子性

// 释放锁时,先比较锁对应的 value 值是否相等,避免锁的误释放
if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])
elsereturn 0
end

图解引自JavaGuide: 

 这样做有两个好处 用value判断是否为对应线程的锁 是则释放 否则error 防止误删其他锁

同时 用lua脚本保持原子操作 确保锁释放不会被其他线程打断

技术往往都是双刃剑 同样这种锁也有一些问题 要是程序遇到问题 比如说释放锁的逻辑突然挂掉可能导致锁无法正常释放 进而造成共享资源无法被其他进程或者线程访问 所以 为了避免这种问题 需要给锁设置一个过期时间

分布式锁设置过期时间

为了避免这种因外部原因或者突然断电异常 可以给这个key(这个分布式锁)设置一个过期时间

127.0.0.1:6379> SET lockKey uniqueValue EX 3 NX
OK

 lockKey:加锁的锁名 uniqueValue 能够唯一标识锁的随机字符串(可以用UUID)

NX:只有当lockKey对应的key值不存在时才能set成功

EX:过期时间设置(秒为单位) EX 3 表示这个锁有三秒的自动过期时间 与EX对应的是PX(毫秒)两个都是过期时间设置

一定需要保证设置指定key的值和过期时间是原子操作!!!!!!否则锁无法被释放

但是我怎么知道要设置多长过期时间 我写完这个文章我难道要给自己设置个过期时间 到九点前我写完了 那我网页还挂着为啥不关机 九点之前没写完 我电脑直接关机了我怎么写

对于这种分布式锁也是一样的道理 一般正常的操作都是毫秒级别 过期时间小于线程对共享资源的操作时间 就回出现锁提前过期的问题 进而导致分布式锁直接失效 过期时间设置过长又会浪费性能

要是能自动设置就好了!

如何实现优雅的续期....?

总会有人想到的 这里就引入Redis里现成的方案 Redission

Redission自动续期机制

Redission就可以做到自动续期机制 其底层实现上 使用了看门狗机制(Watch Dog)

在共享资源的线程操作还未完成的情况下 看门狗会一直延长过期时间(默认情况下存活30s

每10s更新一次过期时间)进而保证锁不会因为超时而被释放

多好的看门狗 多希望自己学习也是这

图解引自JavaGuide: 

 看门狗名字的由来于 getLockWatchdogTimeout() 方法,此方法返回看门狗给锁续期的过期时间(默认30s)

//默认 30秒,支持修改
private long lockWatchdogTimeout = 30 * 1000;public Config setLockWatchdogTimeout(long lockWatchdogTimeout) {this.lockWatchdogTimeout = lockWatchdogTimeout;return this;
}
public long getLockWatchdogTimeout() {return lockWatchdogTimeout;
}

方法内用lua脚本保证操作原子性 renewExpiration方法包含主要逻辑 

private void renewExpiration() {//......Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {@Overridepublic void run(Timeout timeout) throws Exception {//......// 异步续期,基于 Lua 脚本CompletionStage<Boolean> future = renewExpirationAsync(threadId);future.whenComplete((res, e) -> {if (e != null) {// 无法续期log.error("Can't update lock " + getRawName() + " expiration", e);EXPIRATION_RENEWAL_MAP.remove(getEntryName());return;}if (res) {// 递归调用实现续期renewExpiration();} else {// 取消续期cancelExpirationRenewal(null);}});}// 延迟 internalLockLeaseTime/3(默认 10s,也就是 30/3) 再调用}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);ee.setTimeout(task);}

JavaGuide的源码分析很到位 我之前做过看门狗机制的笔记 比较凌乱 基本上是这样的流程

有ttl和time(ttl 锁有效时间 time获取锁的等待时间) 需要确保业务正常运行释放锁  不能因为阻塞异常释放锁 引出看门狗机制

这里的ee对象封装了当前线程id与当前定时的任务(该定时任务里用lua脚本对redis有效期定时更新 在看门狗机制30s的情况下 每30/3 = 10秒后会重启任务 通过内部递归重复调用方法执行)

Watch Dog 通过调用 renewExpirationAsync() 方法实现锁的异步续期:

protected CompletionStage<Boolean> renewExpirationAsync(long threadId) {return evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,// 判断是否为持锁线程,如果是就执行续期操作,就锁的过期时间设置为 30s(默认)"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return 1; " +"end; " +"return 0;",Collections.singletonList(getRawName()),internalLockLeaseTime, getLockName(threadId));
}

 这里的renewExpirationAsync方法实现了续期 用lua脚本主要是保证操作原子性

JavaGuide里用Redission分布式 可重入锁RLock来说明如何使用Redission实现分布式锁

// 1.获取指定的分布式锁对象
RLock lock = redisson.getLock("lock");
// 2.拿锁且不设置锁超时时间,具备 Watch Dog 自动续期机制
lock.lock();
// 3.执行业务
...
// 4.释放锁
lock.unlock();
// 手动给锁设置过期时间,不具备 Watch Dog 自动续期机制
lock.lock(10, TimeUnit.SECONDS);

注意 不设置锁过期时间时 才会出发看门狗自动续期机制

如何实现可重入锁?

什么是可重入锁?

单线程可以多次获取同一把锁 例如一个线程在执行一个带锁的方法 在方法中又调用了另一个同样需要相同锁的方法 则改成可以直接执行调用的方法 即可重入 相同线程多次进方法内的锁 

Java中 synchronized(JVM内置监视器锁)与ReentrantLock(Lock实现类锁)都是可重入锁

不可重入的分布式锁基本可以满足绝大部分场景 特殊情况下还是要用可重入分布式锁

可重入分布式锁实现核心思路线程在获取锁时判断是否为自己的锁 如果是 就不用重新获取 

所以 可以给每个锁关联一个可重入计数器 和 一个 占有他的线程(给线程上标识)当可重入计数器

大于0时 则锁被占有 需要判断占有该所的线程和请求获取锁的线程是否一致(是否有相同标识)

实际上 总有人替你负重前行 Redission内部内置了多种类型的锁比如可重入锁(ReentrantLock)自旋锁(SpinLock) 公平锁(Fair Lock)多重锁(MultiLock)红锁(RedLock)读写锁(ReadWriteLock)

要是都用一个Redis服务 Redis爆炸了怎么办?所以一般都是集群服务 所以..

集群模式下如何实现分布式锁?如何保证分布式锁可靠性?

Redis集群数据同步到每个节点 会是异步还是同步? 一定是异步操作 同步太慢 那如果Redis主节点获取到锁后 在未能同步到其他节点时主服务宕机了.. 此时 新的Redis主节点还是能获取锁  变成新老大 多个应用服务还是可以同时获取锁

图解引自JavaGuide:

针对这种突然崩溃的情况 还是有人替我负重前行 用RedLock(红锁)来解决

RedLock算法思想 是 让客户端向Redis集群中多个独立的Redis实例依次请求申请加锁 若客户端能和多半数实例成功完成加锁操作 那么就宏观上认为:客户端成功获得分布式锁 否则加锁失败

即使部分Redis节点出现问题 只要保证Redis集群中有多半数Redis节点可用 分布式锁服务就ok

RedLock是直接操作Redis节点的 并不是通过Redis集群操作的 这样才可以避免Redis集群主从切换导致锁丢失问题

贴上锁优化时的问题

哨兵会让从节点变为主节点 但是原锁就会失效  可以在新主节点进新锁 会引发线程安全问题

以及从开始到现在我遇到的分布式锁的优缺点和优化过程 今天又看到RedLock

基本上到这里也是个人项目的内容

面试拷打:如何在项目中实现分布式锁?采用了什么方法?

Redission实现分布式锁..后续后面补上

参考资料

[1]Redisson: https://github.com/redisson/redisson

[2]redisson-3.17.6: https://github.com/redisson/redisson/releases/tag/redisson-3.17.6

[3]Redlock 算法: https://redis.io/topics/distlock

[4]How to do distributed locking - Martin Kleppmann - 2016: https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html

[5]JavaGuide:原文链接:支付宝一面:如何基于Redis实现分布式锁?

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Apollo对配置类的热更新优化

背景 关于配置的热更新&#xff0c;apollo 通过com.ctrip.framework.apollo.spring.annotation.SpringValueProcessor 处理带Value的方法或字段&#xff0c;通过监听变更事件&#xff0c;采用反射去更新对应的值 但这个功能仅仅用于单个属性&#xff0c;当我有一组有关联关系…

【AD】3-5 元件在原理图中的基本操作2

1.导线连接 选择放置->线&#xff08;CtrlW&#xff09;&#xff0c;或者直接点击横条处&#xff0c;建议使用直角走线 注意&#xff1a;下图中的线不具有电气连接属性&#xff0c;只是单纯的线 双击导线&#xff0c;进行设置导线粗细及颜色 2.网络标签 放置->网络标…

vim:基础配置

Vim 配置清单 设置行号显示 set number 设置相对行号&#xff08;可选&#xff09; set relativenumber设置制表符为4个空格 set tabstop4 设置自动缩进 set autoindent " 启用语法高亮 syntax on" 设置背景颜色&#xff08;可选&#xff0c;根据终端或GUI Vi…

day02_Java基础

文章目录 day02_Java基础一、今日课程内容二、数组&#xff08;熟悉&#xff09;1、定义格式2、基本使用3、了解数组的内存图介绍4、数组的两个小问题5、数组的常见操作 三、方法&#xff08;熟悉&#xff09;1、定义格式2、方法重载overload 四、面向对象&#xff08;掌握&…

【UCB CS 61B SP24】Lecture 17 - Data Structures 3: B-Trees学习笔记

本文以 2-3-4 树详细讲解了 B 树的概念&#xff0c;逐步分析其操作&#xff0c;并用 Java 实现了标准的 B 树。 1. 2-3 & 2-3-4 Trees 上一节课中讲到的二叉搜索树当数据是随机顺序插入的时候能够使得树变得比较茂密&#xff0c;如下图右侧所示&#xff0c;时间复杂度也就…

【手撕算法】支持向量机(SVM)从入门到实战:数学推导与核技巧揭秘

摘要 支持向量机&#xff08;SVM&#xff09;是机器学习中的经典算法&#xff01;本文将深入解析最大间隔分类原理&#xff0c;手撕对偶问题推导过程&#xff0c;并实战实现非线性分类与图像识别。文中附《统计学习公式手册》及SVM调参指南&#xff0c;助力你掌握这一核心算法…

西门子S7-1200比较指令

西门子S7-1200 PLC比较指令学习笔记 一、比较指令的作用 核心功能&#xff1a;用于比较两个数值的大小或相等性&#xff0c;结果为布尔值&#xff08;True/False&#xff09;。典型应用&#xff1a; 触发条件控制&#xff08;如温度超过阈值启动报警&#xff09;数据筛选&…

solidwork智能尺寸怎么对称尺寸

以构造轴为中心线就能画智能尺寸的对称尺寸。先点击边再点击构造线

深入浅出:Spring AI 集成 DeepSeek 构建智能应用

Spring AI 作为 Java 生态中备受瞩目的 AI 应用开发框架&#xff0c;凭借其简洁的 API 设计和强大的功能&#xff0c;为开发者提供了构建智能应用的强大工具。与此同时&#xff0c;DeepSeek 作为领先的 AI 模型服务提供商&#xff0c;在自然语言处理、计算机视觉等领域展现了卓…

CSS浮动详解

1. 浮动的简介 浮动是用来实现文字环绕图片效果的 2.元素浮动后会有哪些影响 对兄弟元素的影响&#xff1a; 后面的兄弟元素&#xff0c;会占据浮动元素之前的位置&#xff0c;在浮动元素的下面&#xff1b;对前面的兄弟 无影响。 对父元素的影响&#xff1a; 不能撑起父元…

python数据类型等基础语法

目录 字面量 注释 变量 查数据类型 类型转换 算数运算符 字符串定义的三种方式 字符串占位 数据输入 字面量 被写在代码中固定的值 六种数据类型: 1 字符串 String 如"egg" 2 数字 Number: 整数int 浮点数float 复数complex :如43j 布尔…

Android 图片压缩详解

在 Android 开发中,图片压缩是一个重要的优化手段,旨在提升用户体验、减少网络传输量以及降低存储空间占用。以下是几种主流的图片压缩方法,结合原理、使用场景和优缺点进行详细解析。 效果演示 直接先给大家对比几种图片压缩的效果 质量压缩 质量压缩:根据传递进去的质…

【linux】详谈 环境变量

目录 一、基本概念 二、常见的环境变量 取消环境变量 三、获取环境变量 通过代码获取环境变量 环境变量的特性 1. getenv函数:获取指定的环境变量 2. environ获取环境变量 四、本地变量 五、定义环境变量的方法 临时定义&#xff08;仅对当前会话有效&#xff09; 永…

《几何原本》公理

《几何原本》公理 等于同量的量彼此相等 即若 a b , b c ab,bc ab,bc 则 a c ac ac 等量加等量&#xff0c;其和仍相等 即若 a b ab ab 则 a c b c acbc acbc 等量减等量&#xff0c;其差仍相等 即若 a b ab ab 则 a − c b − c a-cb-c a−cb−c 彼此能够&a…

学习路程十一 langchain核心组件 Memory

前序 在最开始我们就通过实验知道LLM 本身是没有记忆的&#xff0c;每一次LLM的API调用都是一个全新的会话。但在某些应用程序中&#xff0c;如&#xff1a;聊天机器人&#xff0c;让LLM记住以前的历史交互是非常重要&#xff0c;无论是在短期的还是长期的。langchain中的“Me…

C语言多级指针详解 - 通过实例理解一级、二级、三级指针

C语言多级指针详解 - 通过实例理解一级、二级、三级指针 文章目录 C语言多级指针详解 - 通过实例理解一级、二级、三级指针引言一、示例代码二、指针基础回顾&#xff08;先备知识&#xff09;1.生活中的类比2.指针变量定义3.关键操作符4.解引用本质 三、多级指针的本质1.层级关…

基于大数据的民宿旅馆消费数据分析系统

【大数据】基于大数据的民宿旅馆消费数据分析系统&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统可以揭示民宿市场的消费模式和价格分布情况&#xff0c;帮助理解消费者偏好、价格走势及…

[特殊字符]【CVPR2024新突破】Logit标准化:知识蒸馏中的自适应温度革命[特殊字符]

文章信息 题目&#xff1a;Logit Standardization in Knowledge Distillation论文地址&#xff1a;paper代码地址&#xff1a;code年份&#xff1a;2024年发表于CVPR 文章主题 文章的核心目标是改进知识蒸馏&#xff08;KD&#xff09;中的一个关键问题&#xff1a;传统KD方…

CAM350_安装

版本&#xff1a;V14.5 一、安装 打开.exe文件 选择不重启&#xff0c;然后再打开这个.exe 再来一次类似的操作 二、配置 复制patch文件夹中的这三个 &#xff0c;粘贴到掉安装目录中 设置ACT_INC_LICENSE_FILE用户环境变量来设置license管理 打开电脑的环境变量 破解完毕&am…

【AD】3-9 物料BOM表的设置与导出

1.报告—… 2.筛选导出内容 3.进行选择导出格式 官方模板 亦可以自行修改其模板&#xff0c;AD官方的BOM表模板在安装目录下的Templates文件夹下&#xff08;C:\Users\Public\Documents\Altium\AD24\Templates&#xff09;