day104-缓存-分布式锁-分布式锁原理与使用

【信息科学与工程学】计算机科学与自动化——第三百零四篇 高性能系统开发指南01 高并发:限流、熔断、降级高性能:红包拆分、多级缓存、大Key/热Key拆分、Pipeline、线程池、RocketMQ零丢失、幂等、分布式锁、集群缩容、分批入库海量数据处理:日志分表分片、跨表查询、冷热分层、大数据引擎服务器选型系统安全:网关安全、服务器安全、云存储安全、风控、监控、测试数据一致性:缓存与数据库一致性、大厂方案、蓝绿回滚、流量一致性、流程一致性项目与团队管理:流程问题、管理问题(风险、成本、人员、文档、培训)稳定性建设:上线三板斧、线上五步闭环、混沌工程、容量规划。 阅读详情

1.原理

分布式锁的一个基本原理就是多个服务内的多个线程访问某资源时,全都到同一个地方占坑,这个地方就是缓存,被所有服务共享的缓存,第一个访问的线程会去缓存中设置

一个key value 缓存,由于是原子性的操作,后面的线程设置同样key时缓存失败,从而实现一个分布式的锁

2.linux命令实现方式

指令参考

http://www.redis.cn/commands/set.html

从上链接可以看到当给set指令添加NX选项时,只有当key不存在时才能缓存该数据

(1)复制四个链接

(2)发送同一条命令连接到客户端(记得先切root权限)

(3)连接上后再次同时发送缓存命令

连接1

连接2,3,4全部是如下界面,都返回nil 也就是null

可以看到NX缓存模式下,key值相同时,只有第一次缓存会成功  这就是分布式锁的基本原理

3.代码实现方式与完善优化

初步实现,核心代码如下,就是在我们一个线程访问进行(查询数据库并保存到数据到缓存)时,给其加上分布式锁,也就是在redis中以NX模式设置一个key为lock的缓存,当其它线程访问时,若设置缓存失败也就是获取分布式锁失败,进行自旋,也就是重复的调用本方法再次进行尝试,当然为了尝试不要太频繁可设置休眠时间

    @Override
    public Map<String, List<Catelog2Vo>> getCatalogJson() {
        //先从缓存中尝试获取,若为空则从数据库中获取然后放入缓存
        String catalogJson;
        synchronized (this) {
            ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
            catalogJson = opsForValue.get("catalogJson");
            System.out.println("从缓存内查询...");
            if (StringUtils.isEmpty(catalogJson)) {
                Map<String, List<Catelog2Vo>> catalogJsonFromDb = null;
                try {
                    catalogJsonFromDb = getCatalogJsonFromDbWithRedisLock();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("从数据库内查询...");
                return catalogJsonFromDb;
            }
        }

        Map<String, List<Catelog2Vo>> catalogJsonFromCache = JSON.parseObject(catalogJson, new TypeReference<Map<String, List<Catelog2Vo>>>() {
        });
        return catalogJsonFromCache;
    }

    public Map<String, List<Catelog2Vo>> getCatalogJsonFromDbWithRedisLock() throws InterruptedException {

        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111");

        if(lock){
            Map<String, List<Catelog2Vo>> dataFromDb = getDataFromDb();
            //删除锁
            stringRedisTemplate.delete("lock");
            return dataFromDb;
        }else{
            Thread.sleep(100);
           return getCatalogJsonFromDbWithRedisLock();
        }

    }

分析下以上实现存在的问题,删除锁之前出现异常或者断电,宕机等情况时会造成死锁

,所以我们给该key(锁)设置一个过期时间

这次由于我们是分开设置的,如果获取锁之后设置过期时间之前断电啥的,也会导致死锁

解决思路:缓存与缓存数据的过期时间设置必须为原子性的,redis支持,所以改为以下代码

        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111",30,TimeUnit.SECONDS);

可是还有问题,当我们查询数据库并添加到缓存或者处理业务逻辑时间过长,超过我们设置的过期时间,此时如果逻辑执行完后删除锁就是删除的别的线程的了

核心代码,值设置为uuid来方便删除时识别是否为当前线程的锁

    public Map<String, List<Catelog2Vo>> getCatalogJsonFromDbWithRedisLock() throws InterruptedException {

        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111",30,TimeUnit.SECONDS);
        String uuid = UUID.randomUUID().toString();
        if(lock){
            Map<String, List<Catelog2Vo>> dataFromDb = getDataFromDb();
            //删除锁 如果是当前线程的才删除 不然可能删的是线程的锁
            String value = stringRedisTemplate.opsForValue().get("lock");
            if(uuid.equals(value)){
                stringRedisTemplate.delete("lock");
            }
            return dataFromDb;
        }else{
            Thread.sleep(100);
           return getCatalogJsonFromDbWithRedisLock();
        }
    }

可是还有问题,由于获取value对比与删除缓存不是原子性操作,获取value之后与删除缓存之前万一宕机停电啥的,也会导致删除别人的锁,相当于没锁住

所以再次对代码进行改造,用redis提供的脚本删锁方式来保证原子性

    public Map<String, List<Catelog2Vo>> getCatalogJsonFromDbWithRedisLock() throws InterruptedException {

        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111",300,TimeUnit.SECONDS);
        String uuid = UUID.randomUUID().toString();
        if(lock){
            Map<String, List<Catelog2Vo>> dataFromDb;
            try {
                dataFromDb = getDataFromDb();
            }finally {
                String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                        "then\n" +
                        "    return redis.call(\"del\",KEYS[1])\n" +
                        "else\n" +
                        "    return 0\n" +
                        "end";
                //保证获取value对比与删除缓存是原子操作,这里采用redis提供的执行脚本方式
                Long lock1 = stringRedisTemplate.execute(new DefaultRedisScript<Long>(), Arrays.asList("lock"), uuid);
            }
            return dataFromDb;
        }else{
            Thread.sleep(200);
           return getCatalogJsonFromDbWithRedisLock();
        }
    }

此时就演进成了阶段五最终形态

至此就基本已经实现并完善了分布式锁,压测这里我就不做了,结果是可以预想的会成功

由于代码的重复性,可以将其封装,当然这些别人都已经做好了,后面将会了解分布式锁更专业的框架--Redisson

本篇主要参考链接:

http://www.redis.cn/commands/set.html

 

【信息科学与工程学】计算机科学与自动化 第二百三十三篇 凤凰架构01 编号类型领域系统软件/硬件或系统集成方案及系统架构模块子模块函数名称/算法名称算法的数学分析及计算机体系架构及数据结构与算法详细设计及参数列表及数值范围设计关联知识1共识算法分布式基石状态机复制架构:Proposer/Acceptor/Learner 多节点角色模型,基于操作转移(Operation Transfer)分布式共识PaxosPaxos 协议(Prepare/Promise/Accept/Accepted 两阶段)数学基础:多数派(Quorum)投票,基于鸽巢原理保证 W+R>N 时读写集合必有 阅读详情

相关推荐

【信息科学与工程学】计算机科学与自动化——第三百零五篇 计算机系统中的各类运算操作01

编号类型领域运算操作运算类型算法算法的详细设计及数学分析参数列表及参数的数值设计应用场景 (100+条)集合运算​1集合运算关系代数并集 (Union)集合运算归并排序去重合并两个集合,去除重复元素。时间复杂度 O(n+m),n,m为两集合大小。输入:集合A, B。输出:A∪B。数据集成、多表结果合并、去重后的全量数据提取。2集合运算关系代数交集 (Intersection)集合运算哈希交集找出同时属于两个集合的元素。平均时间复杂度 O(min(n,m)),最坏 O(n*m)。输入:集合A, B。输出:A∩

weixin_49199313的博客 1389

基于Redis缓存层实现分布式锁

常见分布式锁实现方案 分布式锁原则 1、相互排斥,即任一时刻,只能有一个客户端持有锁; 2、无死锁,持有锁的客户端宕机或网络延迟下仍可获取锁; 3、有始有终,一个客户端加了锁,只能自己释放锁,当然也不能被其他客户端解锁; 4、容错性,只要大部分redis节点还存活,那么客户端就应该可以正常加锁和释放锁; 一、加锁操作 &lt;!--jedis 2.9.0--&gt; &lt;de...

分享传递价值 5403

【信息科学与工程学】计算机科学与自动化-——第十五篇云计算 12 公有云里的“多Region + 多AZ“ 02 运营算法01

算法逐步思考推理思考(含数学方程式、对象、资源、任务、进程/协程/线程及对应的代数、约束、目标函数/传递函数/依赖函数、参数及参数的数值范围及边界条件、逐步推理的数学方程式列表、上下文切换、并发/串行/随机/乱序/顺序)算法逐步思考推理思考(含数学方程式、对象、资源、任务、进程/协程/线程及对应的代数、约束、目标函数/传递函数/依赖函数、参数及参数的数值范围及边界条件、逐步推理的数学方程式列表、上下文切换、并发/串行/随机/乱序/顺序)云厂商提高闲置资源利用率。算法复杂度为O(N),N为实例数量。

weixin_49199313的博客 282

分布式锁的几种实现方式

转自:http://www.hollischuang.com/archives/1716 目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。

zdy0_2004的专栏 1万+

【信息科学与工程学】【数据科学】数据科学领域 第六篇 算法设计 07 堆、栈、表、矩阵、集合、队列、数组、字符串、树、图、动态规划、结构体、元组的算法01

当前段必须包含所有字符的最后出现位置。1. 字符串分割成最多片段2. 基因序列功能域划分3. 文本段落划分4. 音频声道分离5. 图像色彩分区6. 社交网络社群划分7. 蛋白质结构域8. 音乐乐章分段9. 代码模块划分10. 章节划分特征:一次扫描,贪心确定边界。

weixin_49199313的博客 1272

【信息科学与工程学】计算机科学与自动化——第十八篇 存储系统设计 10 存储器/存储软件/存储芯片/存储盘/存储系统/存储网络01

③ MTJ电阻 → 界面:TMR=(R_AP-R_P)/R_P = 2P₁P₂/(1-P₁P₂) (Julliere模型);③ 访问延迟模型 → 统计:平均访问延迟 = p_hot * t_dram + (1-p_hot) * t_nvm + t_migration_overhead;:① 储能电容计算 → 数学物理:所需能量 E = P * t_backup,电容 C = 2E / (V_start² - V_end²);:① B-tree平衡 → 代数:树高 h = log_m(N),m为扇出;

weixin_49199313的博客 263

利用多写Redis实现分布式锁原理与实现分析

在我写这篇文章的时候,其实我还是挺纠结的,因为我这个方案本身也是雕虫小技拿出来显眼肯定会被贻笑大方,但是我最终还是拿出来与大家分享,我本着学习的态度和精神,希望大家能够给与我指导和改进方案。 一、关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到。 我举二个例子:场景一:从前端界面发起一笔支付请...

weixin_34067102的博客 483

【信息科学与工程学】【数据中心】第三十二篇 云数据中心的计算/存储/网络的核心挑战01

状态方程: SOCk+1​=SOCk​−Qn​ik​Δt​;: 三相电流变换到αβ坐标系: iα​=32​​(ia​−21​ib​−21​ic​), iβ​=32​​(23​​ib​−23​​ic​). 计算瞬时有功功率p和无功功率q,经低通滤波器分离直流分量,反变换得基波电流,差值即为谐波指令。: 节点i的信念 bi​(xi​)=ϕi​(xi​)∏j∈N(i)​mji​(xi​), 消息 mij​(xj​)=∑xi​​ϕi​(xi​)ψij​(xi​,xj​)∏k∈N(i)\j​mki​(xi​).

weixin_49199313的博客 219

memcache 缓存 分布式锁原理

//cache.add (key ,value); 如果service1 分布式部署,又同时操作了一个数据库记录,要对某个字段做更新,而这个字段的值,又依赖于之前的值,如何保证数据的正确性? public void sevice1(){ if(cache.add(key,value)){ //可以添加进去,证明是首次调用。 }else{ //不可以添加进去,证明是服务已经

xiaoliuliu2050的专栏 3191

MFC 修改CheckBox的文本颜色方法

设置主题 这一步是必须的,不可省略。 BOOL CMFCApplication1Dlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 // 设置主题 HWND

Kim的博客 2249

芯片散热技术突破:多尺度结构与VC集成方案解析

在半导体技术快速发展的背景下,芯片散热成为制约性能提升的关键瓶颈。传统散热技术如风冷和水冷已接近物理极限,而两相浸没式液冷技术凭借其超高传热系数成为新的解决方案。通过多尺度表面结构设计与真空腔均热板(Vapor Chamber)的集成创新,可以显著提升沸腾性能,降低沸腾起始温差(ONB)并突破临界热流密度(CHF)限制。这种技术在数据中心、高性能计算(HPC)和边缘计算等高热流密度场景中具有重要应用价值,能有效提升能效比(PUE)并降低能耗。

weixin_36288992的博客 155

分布式锁原理介绍

分布式服务中,如果各个服务节点需要竞争资源,不能像单机多线程应用一样使用线程锁,需要由一套分布式锁机制保证节点对资源的访问。通常分布式锁以单独的服务方式实现,目前比较常用的分布式锁实现有三种:zookeeper实现、redis实现和memcache实现。后两者本质上相同。

shelldon的专栏 2987

二、租用AutoDL服务器与连接教程

点击右上角控制台,选择【租用新的实例】;,我这里使用的是xshell和xftp工具(自行下载)。打开本地目录和服务器目录,直接拖动文件。

zzf021122的博客 1327

SQL LIMIT 原理与生产级实践:从分页性能到数据安全控制

LIMIT 是 SQL 中最常用却最易被误解的关键字,它并非简单的结果截断,而是直接影响查询执行计划、索引下推、排序策略乃至事务一致性的底层机制。理解其在 MySQL/PostgreSQL 中如何被优化器识别、为何 OFFSET 会引发 O(N) 性能退化、以及如何与 ORDER BY、索引、MVCC 协同工作,是保障高并发业务稳定的核心能力。本文聚焦 LIMIT 在深度分页、随机采样、API 防遍历等真实场景中的行为边界,结合 EXPLAIN 判读、游标分页迁移、物化视图替代等工程方案,提供可审计、可压测

weixin_30599769的博客 366

DirectX修复工具使用技巧之二——手动修复C++创建失败的文件

最后更新:2021-2-25 随着V4.0正式版的发布,近来有部分用户来咨询如何解决C++文件创建失败的问题。在此我将以解决最常见的C++2015-2019文件创建失败为例,向大家演示一下在线修复的方法,其他C++或文件的方法大同小异。此次操作以Windows 7为例,其他系统相应参考即可。 首先,如果希望程序能手动在线修复创建失败的失败,请首先确定您使用的V4.0.2版或更高版本,老版本不支持此功能。查看程序版本的方式可以把鼠标放在DirectX Repair.e...

VBcom的专栏 6万+

【unity实战】Unity2D TileMap的探究(最简单,最全面的TileMap使用介绍)

之前有人在微信上找我,问了TileMap相关的知识【unity实战】随机地下城生成2——绘制地图Tilemap的使用及一些技巧的使用(含源码)不过写的不是特别不是特别细致,只是简单的用了一下,所有这次特地打算单独写一篇文章,来介绍一下,下次还有人问我,我直接就把这篇文章丢给他,完美!Unity的Tilemap 一般称之为瓦片地图或者平铺地图,是 Unity2017 中新增的功能,主要用于快速编辑 2D 游戏中的场景,通过复用资源的形式提升地图多样性。Tilemap是一个用于创建2D地图的工具。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 1万+

分布式锁原理及实现

[TOC] 一、为什么需要锁? 多任务环境 任务都需要对同一共享资源进行写操作 对资源的访问是互斥的 二、分布式锁方案比较 三、Redis实现分布式锁 1. 单个JVM下实现 代码块加Synchronized锁或者Reentrantlock锁 2.分布式下实现 if(setnx(key,1) == 1){ expire(key,30) ...

至尊宝 2万+

华为OD机试 新系统 双机位C卷 往年真题 机考题库(持续收录中)以及算法考点详解(Python/JS/C/C++)

刷的越多,抽中的概率越大,每一题都有详细的答题思路、详细的代码注释发现新题目,随时更新。

学Java,找哪吒 5万+

用redis操作缓存来实现分布式锁实例

目前几乎所有的大型网站及应用都是采用分布式部署的方式,分布式系统开发带来的优点很多,高可用,高并发,水平扩展,分开部署等。但分布式的开发也带来了一些新问题,有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。在单机环境中,Java中其实提供了很多并发处理相关的API  ,也就是我们常说的“锁”(如synchronized,lock),但是这些API在分布式场景中就无能为力了,也就是说J...

4349

C++多线程 task--std::promise和std::future

本文主要讲解C++11/14/17中所提供的应用于多线程编程中的Task机制。Task可由如下几部分取得而本文主要讲解std::promise和std::future相关的内容。std::promise和std::future通过一个数据管道进行交流,可以将std::promise看作生产者,std::future看作消费者,其两者形成如下的方式交互std::promise本身是一个模版类,通过std::promise可以像数据管道中设置value,异常和相应的通知信息。std::promise提供了如下接

qls315的博客 1818

【Spring Cloud Alibaba】Linux安装RocketMQ以及RocketMQ Dashboard可视化工具

在当今分布式架构的开发中,消息队列成为了一种常见的解决方案。RocketMQ是阿里巴巴开源的分布式消息中间件,具有高吞吐量、高可用性和强大的扩展性,因此在构建大规模、高可靠性的应用系统时备受推崇。而在使用RocketMQ时,为了更好地了解消息的流动和监控,RocketMQ Dashboard成为了一个不可或缺的可视化工具。本篇博客将带大家一步步在Linux系统中安装RocketMQ,并介绍如何配置和使用RocketMQ Dashboard。

编码人生 3385

redisson 是锁

Redisson概述 Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Sem...

weixin_41131125的博客 307

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

day100-缓存-缓存使用-压力测试出的内存泄露及解决(Lettuce低版本bug问题解决)

1.压测 查看聚合报告 填表发现使用缓存后,获取三级分类比之前快了很多,吞吐量多了几百 2.出现内存泄露问题分析及解决 我上面压测没报错是因为我使用的springboot版本高点,视频内的springboot版本低些,所以带的lettuce版本也低,该lettuce有个bug,会报下面错误,直接内存不够分配 分析:解决方式有两种,使用高版本lettuce,或者切换使用jedis 使用jedis前记得屏蔽data-redis内携带的lettuce依赖,jedis版本无需指名,...

JavaCoder_juejue的博客 1696

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

144-商城业务-购物车-购物车流程总结

1.首先 离线状态 与 登录状态 分为 离线状态 与 登录状态 离线状态 设计是 模仿京东 在浏览器设置一个cookie 过期时间一个月 值整个唯一的就行 比如 UUID 然后 购物车 的在 redis 中存储数据类型是 hash 对应 java中类型是 Map<String,Map<String,CartItem>> 外层key是 购物车id cart:用户id( 如果未登录 用户id取cookie的值) 内层key是 skuid 然后值是订单项 2.拦截器 ...

JavaCoder_juejue的博客 1189

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

分布式缓存技术-redis应用篇 (redisson ,数据一致性(缓存穿透,雪崩,击穿等问题),布隆过滤器 )

一、redis客户端工具哪家强? 你可能听过jedis,你可能也听过lettuce ,但是redisson 这玩意 相当于把java中的常用数据结构比如下面的集合,还有锁都在redis中实现了,集群啊啥的他也都支持 分布式环境下 就用它了 主要它还是我们中国人写的 链接如右redisson使用链接 ...

JavaCoder_juejue的博客 749

谷粒商城day06-docker安装Redis

还是像往常一样,su root 输入密码vagrant 以切换到root用户 1.下载redis镜像(这里不指定版本就默认下载最新版) docker pull redis 2.创建文件以做配置 mkdir -p /mydata/redis/conf touch /mydata/redis/conf/redis.conf 3.在上面添加的配置文件中添加配置信息,进行端口映射,命名以及目录挂载,及启动时默认加载配置文件的设置 docker run -p 6379:6379 --nam...

JavaCoder_juejue的博客 743
上一篇: day103-缓存-缓存使用-本地锁在分布式下的问题
下一篇: day105-缓存-分布式锁-Redisson简介&整合
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我才是真的封不觉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值