redis缓存数据库详解(五):分布式锁的实现

Redis 缓存 & 分布式锁 将定期生成和实时生成结合一下,先通过“离线”的方式,通过统计的途径,先找到一些热点数据(这些数据并一定精准,有就行)导入到Redis中,就能帮助MySQL承担很大的压力,随之时间的推移,热点数据会逐渐调整,来使用当前情况;Redis服务器首次接入连接之后是没有数据的,此时所有的请求将在MySQL中(就怕此时MySQL没有抗住这么多请求挂了),那么随之时间的推移,Redis上的数据慢慢越来越多,MySQL承担的压力就会小很多;分布式系统中任何⼀个节点都是不可靠的. 最终的加锁成功结论是 “少数服从多数的”. 阅读详情

作为一个稀有的Java妹子,所写的所有博客都只是当作自己的笔记,留下证据自己之前是有用心学习的~哈哈哈哈(如果有不对的地方,也请大家指出,不要悄悄咪咪的不告诉我)

1.锁

锁是为了控制高并发而产生的,多个线程访问同一个资源、用户多次提交等等都是高并发的情况,带来的影响就是数据不正确。比如提交订单,用户点击很多次,如果代码里不加控制,就会产生多条订单数据。
线程锁:主要用来给方法、代码块加锁。当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或该代码段。线程锁只在同一JVM中有效果,因为线程锁的实现在根本上是依靠线程之间共享内存实现的,比如synchronized是共享对象头,显示锁Lock是共享某个变量
分布式锁:随着分布式系统的流行,应用被拆分为许多子系统,每个子系统都可能被部署在不同的服务器上,jvm就不止一个,synchronized就不适用于分布式应用了。所以就会考虑在应用服务器之外有一个存储服务器,存储锁信息,是不是就马上想到了redis。还有其他的分布式锁的解决方案,比如zookeeper等。

2.分布式锁的特征

1.锁应当有过期时间,避免加锁服务挂掉后没有释放锁造成死锁。
2.同一时刻只能有一个线程获取到锁。
3.线程只能解自己加的锁

3.redis实现分布式锁

之前的文章中有介绍redis的一个命令,setnx:set if not exists。当key不存在时保存,存在则什么也不做,利用这个命令就可以实现分布式锁。加锁和解锁都是用luna表达式,因为luna表达式可以原子性的执行命令,因为setnx是先set再设置过期时间,如果出现set完后,服务器宕机,过期时间还没设置,就产生了死锁。
加锁的luna语句为:执行成功返回1,表明加锁成功,否则失败,即redis中已存在相同的key了。

 "local key = KEYS[1]; local value = ARGV[1]; if redis.call('set', key, value,
 'NX' ,'PX', "+ millisecond + ") then return 1 else return 0 end";

解锁的luna语句:查询是否存在key,存在则删除,使用luna保持原子性

 "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) 
 else return 0 end";

完整的service:

@Component
public class RedisLockService {
    private static final Long RELEASE_SUCCESS = 1L;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     *  加锁
     * @param key key
     * @param value value
     * @param millisecond millisecond
     * @param tryCount tryCount
     * @return 状态
     */
    public boolean lock(String key, String value, int millisecond, int tryCount) {
        // 重试次数最大5次
        if (tryCount > 5) {
            tryCount = 5;
        }
        try {
            String script =
                    "local key = KEYS[1]; local value = ARGV[1]; if redis.call('set', key, value, 'NX' ,'PX', "
                            + millisecond + ") then return 1 else return 0 end";
            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
            Long result = redisTemplate
                    .execute(redisScript, Collections.singletonList(key), Collections.singletonList(value));
            // 判断结果
            if (RELEASE_SUCCESS.equals(result)) {
                return true;
            }
        } catch (Exception ex) {
            // redis命中失败或连接有问题时,重试机制
            // 重试次数
            if (tryCount == 0) {
                return false;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                return false;
            }
            System.out.println(tryCount);
            // 重试机制
            return lock(key, value, millisecond, tryCount - 1);
        }
        return false;
    }
    /**
     * 释放分布式锁
     *
     * @param lockKey 锁
     * @param value   请求标识
     * @return 是否释放成功
     */
    public boolean releaseLock(String lockKey, String value) {

        String script =
                "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        Long execute =
                redisTemplate.execute(redisScript, Collections.singletonList(lockKey), Collections.singletonList(value));
        if (RELEASE_SUCCESS.equals(execute)) {
            return true;
        }
        return false;

    }
}

4.模拟一下并发情况

public void testRedisLockService() throws InterruptedException {
		//加锁,设置过期时间,这里只是为了演示,设置得很大
        boolean lock = redisLockService.lock("testLock", "testLock", 1000000000, 0);
        try{
            if(lock){
            	log.info("拿到锁,正在处理业务!");
            	//模拟业务处理,占用锁资源
                Thread.sleep(1000000000);
            }else {
                System.out.println("锁已被占用");
            }
        }catch (Exception e){
            log.info(e.getMessage());
        }finally {
        	//记得释放锁资源
        	if (lock) {
            	redisLockService.releaseLock("testLock","testLock");
            }
        }
        
    }

启动一个线程调用testRedisLockService方法:

在这里插入图片描述
再启动一个线程调用testRedisLockService方法:
在这里插入图片描述
redis锁起效。

5.注意

在生产环境中,锁的过期时间应该根据业务场景来决定,业务处理完后一定要释放锁资源。

6.缓存穿透、缓存雪崩、缓存击穿

虽然缓存可以提高应用的响应时间,但是还是会有一些潜在的问题,比如数据的一致性,视频的点赞量,缓存里的数据跟数据库的数据肯定不是时时都是一致的,数据库是定时的去同步缓存里的数据,所以是存在数据不一致的。
一般来讲,查询某个数据是先查缓存,如果有则直接返回,没有则查询数据库,数据库中存在则放入缓存,没有则什么也不做。

缓存穿透:指查询时,缓存里没有这个数据,数据库里也没有,那么每次查询都会读库,因为数据库不存在则不会放入缓存,如果攻击者就一直查询这个数据,就会一直访问数据库。比如根据ID查询用户身份,传的是用户ID,用户ID是正整数,如果传一个为-1的就会一直查询数据库,缓存就没有起作用,穿透缓存直接查数据库。

缓存雪崩:某一个时期,缓存里的值集体过期,那么这个时期就会出现数据库压力突然增加的情况,或者是缓存服务器挂机也会出现这种情况。在设置的时候,热点数据过期时间要错开设置过期时间。

缓存击穿:当一个热点key一直被访问,一直承受很大的访问压力,突然这个key不见了,一瞬间所有的压力就落到数据库上,像是在缓存上一个孔被击穿,给数据库造成压力

从零上手:基于英飞凌TC377的UDE STK 5.0实战调试指南 本文详细介绍了基于英飞凌TC377芯片和UDE STK 5.0的实战调试指南,涵盖硬件连接、工作空间配置、程序烧录、多核调试等关键步骤。特别针对汽车电子领域常见的多核架构调试难题,提供了断点设置、变量监控等实用技巧,帮助开发者快速上手并优化调试效率。 阅读详情

相关推荐

Revit二次开发之获取钢筋(Rebar)的线(Line)

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Mar

qq_25986069的博客 1311

Redis(四)- 分布式锁

Redis - 分布式锁问题最佳实践

Eriksen的博客 1671

从零搭建UVM验证环境:新手入门实践指南

在数字芯片验证领域,SystemVerilog和UVM(Universal Verification Methodology)是构建高效验证环境的核心技术。其原理基于面向对象编程和事务级建模,通过组件化架构实现验证的复用性和自动化。这一方法学的技术价值在于显著提升验证效率与质量,广泛应用于处理器、通信芯片等复杂设计的验证场景。本文聚焦于UVM验证环境的搭建实践,通过一个加法器实例,详细解析了uvm_driver、uvm_monitor、uvm_sequence等关键组件的协同工作机制,并提供了基于Quest

weixin_30015371的博客 445

redis加锁的几种方法

1. redis加锁分类   redis能用的的加锁命令分表是INCR、SETNX、SET 2. 第一种锁命令INCR   这种加锁的思路是, key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作进行加一。   然后其它用户在执行 INCR 操作进行加一时,如果返回的数大于 1 ,说明这个锁正在被使用当中。 复制代码 1、 客户端A请求服务器获取key的值为1表示获取了锁 2、 客户端B也去请求服务器获取key的值为2表示获取锁失败 3、 客户端A执

weixin_57763462的博客 3590

量化回测框架三选一:Backtrader、Zipline、PyAlgoTrade,新手到底该跟谁学?

本文对比分析了三大量化回测框架Backtrader、Zipline和PyAlgoTrade,帮助新手选择最适合的入门工具。从安装配置、策略编写到文档查阅,详细解析各框架的特点和适用场景,为量化交易初学者提供实用指南。

weixin_28745309的博客 267

2025年新发表的语音大模型全双工模型/框架/技术

参考该论文,给出2025年新发表的全双工模型或框架或技术下面这份清单专挑、且与“全双工语音对话/同步对话(边听边说、可打断、回馈/相槌、重叠语音)”直接相关的。我也结合你提到的的四大维度(暂停处理、相槌、顺滑换手、打断管理)来点出它们各自的贡献点,便于后续评测与对比。

u013250861的博客 629

再谈分布式锁之剖析Redis实现

之前笔者已经写过关于分布式锁的内容,但囿于彼时对于分布式锁的研究还不算太深入,如今读来发现还是存在一些问题,故而写作本文,对Redis分布式锁的实现做一个更加全面、进阶的阐述和总结,帮助读者对Redis分布式锁有一个更加深入客观的了解。关于更多分布式锁的其他实现,在后续的文章中也会陆续展开。 我们还是通过经典的WWH(what why how)三段论方式进行行文。首先再次从宏观上了解什么是分布式锁...

weixin_30364325的博客 375

零基础学习CANoe Panel(5)——改变变量的值,控件图像也改变,这是怎么回事?

Panel 中控件的图像随着值变化,是怎么回事?

蚂蚁小兵 2144

redis缓存实现分布式锁(附代码)

Redis实现分布式锁主要利用到他的setnX来实现分布式锁。当然,这种情况只是在redis的客户端操作下,如果用的是jds等这种链接代码和redis的话要另外看具体是哪个中间件返回什么值。3.基于redis缓存的方式去实现。redis的分布式锁主要利用他的put和st去实现。释放锁的前提是当前的线程占用了锁,所以直接读取值,然后拿到threadlocal存的线程唯一标记,进行比较,如果相等,则释放锁。如果不相等,则意味着是其他线程来释放锁(这就是实现线程身份校验功能,防止其他线程释放锁),直接返回失败。

weixin_69918608的博客 1613

VTK 显示大量点云数据及交互(点云拾取、着色、测量等)功能

VTK (Visualization Toolkit) 是一个强大的开源可视化库,非常适合处理点云数据。下面将介绍如何使用 VTK 显示大量点云数据,并实现点云拾取、着色、测量等功能。

深之JohnChen的专栏 1992

使用Simulink进行四足机器人的非线性动力学建模与仿真

四足机器人的运动包含多个自由度(DOF),包括腿部的摆动和支撑阶段的动力学特性。由于存在接触力、摩擦以及不同地形的影响,四足机器人的动力学行为呈现出高度的非线性特征。因此,构建准确的非线性动力学模型对于研究机器人的运动学和动力学特性至关重要。定义代表机器人状态的变量,如位置、速度、角度和角速度。matlab深色版本通过本指南,我们介绍了如何基于Simulink搭建一个四足机器人的非线性动力学模型,并进行了详细分析。

xiaoheshang_123的博客 311

redis加锁

redis 分布式锁

zlfjavahome的专栏 1097

从零开始学 AUTOSAR:深入浅出 SAE J1939 诊断通信管理器(J1939Dcm)深度完整版

SAE J1939 是美国汽车工程师学会制定的重型车辆 CAN 总线通信标准,覆盖卡车、客车、工程机械、农机、船舶等,是商用车领域的 “通用语言”。基于CAN 2.0B 扩展帧(29 位 ID)(车规主流)PGN(参数组号)+ SPN(可疑参数号)核心标准:J1939-21:数据链路层(CAN 帧格式、地址声明)J1939-71:应用层(普通信号、报文)诊断层(核心!J1939Dcm 依据)J1939-81:网络管理(地址管理、节点检测)

getusushu的博客 110

Redis——缓存穿透、缓存击穿、缓存雪崩、分布式锁

Redis——缓存穿透、缓存击穿、缓存雪崩、分布式锁

贝尔摩德苦艾酒 1万+

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Redis的分布式锁详解

1、什么是分布式锁: 为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度。而这个分布式协调技术的核心就是来实现这个分布式锁。 2、分布式锁应该具备哪些条件 在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行 高可用的获取锁与释放锁 高性能的获取锁与释放锁 具备可重入特性(可理解为重新进入,由多于一个任务并发使用,而不必担心数据错误) 具备锁失效机制,防止死锁 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败 分布式锁的实.

张维鹏的博客 9万+

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

【Redis】缓存+分布式锁

Redis缓存+分布式锁

chaodddddd的博客 4271

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

【Redis经典面试题五】实现分布式锁有几种方式?

分布式锁有多种实现方式,比较常见的实现是通过数据库、Redis或者Zookeeper实现。其中数据库的实现可以依赖悲观锁以及数据库表记录来实现,通过Redis的实现可以考虑使用setnx、redission以及redlock实现。使用zk主要是依赖他提供的临时有序节点来实现。

qq_38196449的博客 3378

Redis进阶:持久化机制、缓存DB一致性、缓存隐患、分布式锁

客户端会向 Redis 集群中多个独立的 Redis 实例节点请求加锁,如果客户端能够从半数以上的 Redis 实例节点申请加锁成功,我们则认为客户端获取分布式锁成功,反之,则认为获取分布式锁失败!:把所有可能存在的请求的值都存放在布隆过滤器中,当用户请求过来,先判断用户发来的请求的值是否存在于布隆过滤器中。在某些分布式缓存系统中,数据可能有多个副本,第一次删除可能没有移除所有的副本。aof是基于内存中的数据进行重写的,重写的时候会把那些历史的无用命令都清除掉,只根据当前一刻内存的数据的最终命令进行重写。

m0_63432840的博客 1108

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

redis是基于内存读写的数据库,因此它的读写速度非常快。常用于缓存、队列、分布式锁等场景。Redis中还有很多数据结构类型:String 、Set、 Hash、 List、ZSet(有序集合)、Bitmaps(位图) HyperLogLog(基数统计)、GEO(地理信息)、Stream(流)。我们将数据存入到redis当中,之后的每次请求都先去redis当中找是否存在数据。从而提高了查询速度也降低了数据库的压力。为了保证redis和数据库的数据一致性,我们通常会给其设置过期时间。

2302_81153890的博客 6600

[redis进阶六]详解redis作为缓存&&分布式锁

缓存(cache)是计算机中的⼀个经典的概念.在很多场景中都会涉及到.核⼼思路就是把⼀些常⽤的数据放到触⼿可及(访问速度更快)的地⽅,⽅便随时读取.对于计算机硬件来说,往往访问速度越快的设备,成本越⾼,存储空间越⼩.缓存是更快,但是空间上往往是不⾜的.因此⼤部分的时候,缓存只放⼀些热点数据(访问频繁的数据),就⾮常有⽤了.在⼀个分布式的系统中,也会涉及到多个节点访问同⼀个公共资源的情况.此时就需要通过锁来做互斥控制,避免出现类似于"线程安全"的问题.

2301_78182852的博客 1119

【Redis十二】Redis的典型应用(缓存和分布式锁)

缓存 (cache) 是计算机中的⼀个经典的概念. 在很多场景中都会涉及到。核⼼思路就是把⼀些常⽤的数据放到触⼿可及(访问速度更快)的地⽅, ⽅便随时读取。对于计算机硬件来说, 往往访问速度越快的设备, 成本越⾼, 存储空间越⼩.缓存是更快, 但是空间上往往是不⾜的. 因此⼤部分的时候, 缓存只放⼀些 热点数据 (访问频繁的数据),就⾮常有⽤了。

2203_75565612的博客 3932

【Redis】缓存和分布式锁

分布式锁其实就是,控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。

hhj25802580的博客 1291

【Redis】redis用作缓存和分布式锁

缓存预热、击穿、雪崩、穿透以及看门狗等相关问题

weixin_69380220的博客 1561

Redis分布式锁防止缓存击穿

缓存击穿 和缓存穿透不同的是,缓存击穿是指:缓存中没有,但是数据库中存在的热点数据。 例如:首页的热点新闻,并发访问量非常大的热点数据,如果缓存过期失效,服务器会去查询DB,这时候如果大量的并发去查询DB,可能会瞬间压垮DB。 画了个简图,如下所示: 解决方案:DB查询加分布式锁。 未加锁的情况 解决问题之前,先看一下不做处理的代码和运行情况。 根据商品ID查询商品详情代码 清空Redis缓存...

程序员小潘 5388

高并发缓存问题分析以及分布式锁的实现

经过分析发现分布式锁的实现在一定程度上还是会存在一系列的问题,通过这些问题的解决可以在很大程度的避免数据不一致的情况,关于分布式锁的实现可以抽取为模板,有业务场景需要的时候二次修改复用即可.

豆豆的博客 2717

Redis核心数据结构与分布式锁实现详解

Redis核心数据结构与分布式锁实现摘要 本文详细介绍了Redis的常用数据结构(字符串、哈希、列表、集合、有序集合)及其应用场景,并提供了Python操作示例。重点讲解了基于Redis的分布式锁实现方案,包括基本SETNX命令、SET扩展命令以及Redlock算法三种实现方式。分布式锁需要满足互斥性、避免死锁、容错性和自旋等待等要求。文章通过代码示例展示了如何获取和释放锁,并分析了不同方案的优缺点。Redis因其丰富的数据结构和原子性操作,成为实现分布式系统协调的理想选择。

私聊前往站内信:https://i.youkuaiyun.com/#/msg/chat/weixin_44976692 2221
上一篇: redis缓存数据库详解(四):redis与springboot整合
下一篇: redis缓存数据库详解(六):高可用方案
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值