#Redis黑马点评#(五)Redisson原理详解

news2025/7/20 8:25:09

目录

一 基于Redis的分布式锁优化

二 Redisson

1 实现步骤

2 Redisson可重入锁机制

3 Redisson可重试机制

4 Redisson超时释放机制

5 RedissonMultiLock解决主从一致性

三 trylock与lock两者有何区别

四 Redis优化秒杀


一 基于Redis的分布式锁优化

二 Redisson

Redisson是一个在Redis的基础上实现的Java驻Java内存数据网格。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务,其中就包含了各种分布式锁的实现。

1 实现步骤

1 导入依赖

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.13.6</version>
        </dependency>

2 配置Redisson客户端

package com.hmdp.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.100.100:6379").setPassword("tan2179432748");
        return Redisson.create(config);
    }
}

3 使用Redisson的分布式锁

2 Redisson可重入锁机制

解决的问题:

  • 当你在一个方法内多次获取同一把锁(比如说递归调用),如果锁不可重入,会出现死锁,线程将自己锁死。

实现原理

  • 可重入锁作用对象指的是同一线程,使用的是Redis当中的Hash数据结构,存储一个计数器,当一个锁进入开始执行的时候,就将计数器加1,如果该锁执行结束就将计数器减1(为0时删除),这样避免了可重入锁之间锁的误删问题。

使用的技术

  • 使用Hash的结构:Key是锁名称,Field是线程唯一标识,Value是重入次数。
  • 通过使用Lua脚本保证操作的原子性。

3 Redisson可重试机制

要解决的问题

当多个线程竞争同一把锁时,失败线程如果直接放弃,可能会导致业务无法正常执行,与设计的业务逻辑出现差错,但是如果无脑循环重试,又会浪费CPU的资源,增加Redis的压力。

解决方案:订阅发布机制+信号量控制+指数退避策略

1 订阅发布机制

  1. 订阅锁释放事件

    • 线程获取锁失败后,订阅 Redis 的特定频道(redisson_lock__channel:{锁名称}),进入等待状态。

    • 不占用 CPU:通过 Redis 的 Pub/Sub 模型 阻塞监听,而非主动轮询。

  2. 锁释放通知

    • 当持有锁的线程释放锁时,向该频道发送解锁消息。

    • 所有订阅该频道的线程会被唤醒,立即重试获取锁

  3. 重试次数控制

    • 默认无限重试(直到成功),可通过trylock(...) 指定最大等待时间。

2 信号量控制

使用Semaphore限制阻塞 -- 同时尝试获取锁的线程数量,避免Redis瞬时压力过大。

3 指数退让策略

每次重试的等待时间按指数增长,减少无效次数

4 Redisson超时释放机制

解决的问题:

  • 如果线程获取锁之后崩溃,没有释放锁,其他线程将永远无法获取锁,出现死锁现象。

核心逻辑:

1 锁续期触发条件:

  1. 当使用lock()不超过指定时间时,Redisson默认启用看门狗机制。
  2. 若手动指定超时时间lock(10,...)则不会启用看门狗,锁到期自动释放。

2 守护线程工作流程:

  1. 初始化:加锁成功后,启动一个后台守护线程。(非业务线程)
  2. 定期检查:每十秒检查一次业务线程是否仍然在执行(通过线程存活状态来判断)
  3. 锁续期:若业务未完成,通过Lua脚本重置锁的超时时间为30秒(维持锁的持有状态)

看门狗机制本质上就是一个续期机制,如果不调用 unlock(),看门狗会无限续期锁,直到持有线程异常终止或者执行unlock(),与业务是否完成无关。锁的续期不依赖业务是否执行完毕,而是依赖显式的 unlock() 调用

后果:线程内不管有没有有业务执行只要没有unlock()释放,就会持续续期,将会在当前线程出现假死锁情况

5 RedissonMultiLock解决主从一致性

解决的问题:

  • 在 Redis 的主从架构中,若主节点加锁后未及时同步到从节点即崩溃(触发哨兵机制),新主节点因无锁记录导致锁状态丢失,其他客户端可重复获取同一把锁,引发数据冲突。

解决方案:

  • 并行加锁:向所有独立主节点发送加锁请求(相同锁名称 + 唯一线程标识)。

解决方案:

Redisson的运作流程

1 当客户端线程调用tryLock()传参方法申请加锁时(支持传入waitTime等待时长、leaseTime自动释放时间及TimeUnit时间单位)我们就会去获取锁,返回值为Boolean值,通过Redis的EXISTS命令检测锁Key,若不存在则表示当前线程为首个锁持有者,采用Redis Hash结构存储锁元数据,通过key-field-value三元组实现可重入计数。若锁Key已存在,通过对比Hash字段中的客户端UUID+线程ID验证线程身份,若是当前线程则执行重入操作,将重入计数器值递增,并刷新锁过期时间为默认30秒(可通过leaseTime配置);若非当前线程,那就会通过PTTL命令获取锁剩余存活时间,与用户设定的waitTime进行超时判定,若未超期,订阅锁释放的Redis频道(可重试性),结合subscribe/publish实现线程阻塞控制​​​​​​​,实现线程的等待,当执行的线程释放之后再去唤醒等待线程;若超过waitTime直接返回获取锁失败。

:传参的leaseTime自动施放时间只会在两种情况刷新一种是初始化,一种是线程重入。

:传参情况不会有看门狗自动续期的操作

2 当客户端线程调用tryLock()不传参方法申请加锁时:前面的校验相同,其核心区别不具有可重试性(没有waitTime)与超时自动释放机制(使用的是看门狗机制)。

:不具备可重试性的体现,在不是当前线程时就会直接返回false,而具备的会触发订阅加锁机制与线程阻塞机制。

:看门狗机制本质上就是一个续期机制,在不传入自动释放时间参数且不调用 unlock(),看门狗会无限续期锁,直到持有线程异常终止或者执行unlock(),与业务是否完成无关。锁的续期不依赖业务是否执行完毕,而是依赖显式的 unlock() 调用。

:可重入的对象是同一线程,重试性的条件是waitTime这个参数

三 trylock与lock两者有何区别

在Redisson中lock与trylock是两种不同的分布式锁实现方式,他们的核心区别在于阻塞行为,失败策略,参数配置。

首先lock是没有返回值的,如果没有加参数,就会持续阻塞等待,直到获取锁,同时必须手动unlock释放。其次trylock是由返回值的,其返回值是一个boolean的返回值,会尝试获取锁,如果没加参数获取失败就会返回false,加了参数会在waitTime等待时间范围之内进行等待,同时会在leaseTime自动释放不会使用看门狗机制。

特性无参 lock()带参 lock(leaseTime)无参 tryLock()带参 tryLock(waitTime, leaseTime)
阻塞行为无限阻塞无限阻塞立即返回在 waitTime 内阻塞
锁持有时间看门狗续期(默认30秒)固定 leaseTime(禁用看门狗)看门狗续期固定 leaseTime(禁用看门狗)
可重试性无限重试无限重试无重试在 waitTime 内重试
是否自动释放否(需手动解锁)是(按 leaseTime 释放)否(需手动解锁)是(按 leaseTime 释放)
适用场景强一致性长任务短时任务,需自动释放快速失败平衡等待时间和可靠性

四 Redis优化秒杀

大体思路:判断库存与判断用户是否下过单在Redis的Lua脚本当中进行判断,再异步开启一个线程,来操作Mysql数据库。

需求:

  • 1. 新增秒杀优惠券的同时,将优惠券信息保存到Redis中  
    @Override
    @Transactional
    public void addSeckillVoucher(Voucher voucher) {
        // 保存优惠券
        save(voucher);
        // 保存秒杀信息
        SeckillVoucher seckillVoucher = new SeckillVoucher();
        seckillVoucher.setVoucherId(voucher.getId());
        seckillVoucher.setStock(voucher.getStock());
        seckillVoucher.setBeginTime(voucher.getBeginTime());
        seckillVoucher.setEndTime(voucher.getEndTime());
        seckillVoucherService.save(seckillVoucher);
        //保存秒杀库存到Redis中
        stringRedisTemplate.opsForValue()
                .set(RedisConstants.SECKILL_STOCK_KEY + voucher.getId(), voucher.getStock().toString());

    }
  • 2. 基于Lua脚本,判断秒杀库存、一人一单,决定用户是否抢购成功  
--1参数列表
--1.1优惠券id
local voucherId = ARGV[1]
--1.2用户id
local userId = ARGV[2]
--2数据key
--2.1库存key
local stockKey = 'seckill:stock:' .. voucherId
--2.2订单key
local orderKey = 'seckill:order:' .. userId
--3 脚本业务
--3.1判断库存是否充足 get stockKey
if (tonumber(redis.call('get', stockKey)) <= 0) then
    --库存不足,返回1
    return 1
end
--3.2判断用户是否重复下单 sismember orderKey userId
if (redis.call('sismember', orderKey, userId) == 1) then
    --用户重复下单,返回2
    return 2
end
--3.3扣减库存 incrby stockKey -1
redis.call('incrby', stockKey, -1)
--3.4下单保存用户 sadd orderKey userId
redis.call('sadd', orderKey, userId)
return 0
  • 3. 如果抢购成功,将优惠券id和用户id封装后存入阻塞队列  
  • 4. 开启线程任务,不断从阻塞队列中获取信息,实现异步下单功能  

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

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

相关文章

23.(vue3.x+vite)引入组件并动态切换(component)

让多个组件使用同一个挂载点,并动态切换,这就是动态组件 效果截图 A组件代码: <template><div><div>{{message }}</</

VBA会被Python代替吗

VBA不会完全被Python取代、但Python在自动化、数据分析与跨平台开发等方面的优势使其越来越受欢迎、两者将长期并存且各具优势。 Python以其易于学习的语法、强大的开源生态系统和跨平台支持&#xff0c;逐渐成为自动化和数据分析领域的主流工具。然而&#xff0c;VBA依旧在Exc…

SEMI E40-0200 STANDARD FOR PROCESSING MANAGEMENT(加工管理标准)-(三)完结

10 消息服务详情 10.1 本章定义实现加工管理概念所需的消息服务。这些消息已在第8.1节中初步介绍。 协议无关性&#xff1a;这些服务独立于所使用的消息协议&#xff0c;可映射至SECS-II&#xff08;SEMI E5&#xff09;或其他类似协议。 10.1.1 消息服务定义内容包括&#…

MySQL数据库创建、删除、修改

一&#xff1a;建库建表 我们以学校体系进行建表。将数据库命名为school。 以下代码中的大写均可小写不影响。如CREATE DATABASE与create database相同 四个关键的实体分别是学院、老师、学生和课程&#xff0c;其中&#xff0c;学生跟学院是从属关系&#xff0c;这个关系从…

【氮化镓】GaN在不同电子能量损失的SHI辐射下的损伤

该文的主要发现和结论如下: GaN的再结晶特性 :GaN在离子撞击区域具有较高的再结晶倾向,这导致其形成永久损伤的阈值较高。在所有研究的电子能量损失 regime 下,GaN都表现出这种倾向,但在电子能量损失增加时,其效率会降低,尤其是在材料发生解离并形成N₂气泡时。 能量损失…

防火墙来回路径不一致导致的业务异常

案例拓扑&#xff1a; 拓扑描述&#xff1a; 服务器有2块网卡&#xff0c;内网网卡2.2.2.1/24 网关2.2.254 提供内网用户访问&#xff1b; 外网网卡1.1.1.1/24&#xff0c;外网网关1.1.1.254 80端口映射到公网 这个时候服务器有2条默认路由&#xff0c;分布是0.0.0.0 0.0.0.0 1…

WTK6900C-48L:离线语音芯片重构玩具DNA,从“按键操控”到“声控陪伴”的交互跃迁

一&#xff1a;开发背景 随着消费升级和AI技术进步&#xff0c;传统玩具的机械式互动已难以满足市场需求。语音控制芯片的引入使玩具实现了从被动玩耍到智能交互的跨越式发展。通过集成高性价比的语音识别芯片&#xff0c;现代智能玩具不仅能精准响应儿童指令&#xff0c;还能实…

Python 数据分析与可视化:开启数据洞察之旅(5/10)

一、Python 数据分析与可视化简介 在当今数字化时代&#xff0c;数据就像一座蕴藏无限价值的宝藏&#xff0c;等待着我们去挖掘和探索。而 Python&#xff0c;作为数据科学领域的明星语言&#xff0c;凭借其丰富的库和强大的功能&#xff0c;成为了开启这座宝藏的关键钥匙&…

gitkraken 使用教程

一、安装教程 安装6.5.3&#xff0c;之后是收费的&#xff0c;Windows版免安装 二、使用教程 0. 软件说明 gitkraken是一个git本地仓库管理软件&#xff0c;可以管理多个仓库&#xff0c;并且仓库可以属于多个网站多个账户。 1. 克隆仓库 选择要克隆到什么位置&#xff0…

【LeetCode 热题 100】二叉树 系列

&#x1f4c1; 104. 二叉树的最大深度 深度就是树的高度&#xff0c;即只要左右子树其中有一个不为空&#xff0c;就继续往下递归&#xff0c;知道节点为空&#xff0c;向上返回。 int maxDepth(TreeNode* root) {if(root nullptr)return 0;return max(maxDepth(root->lef…

用drawdb.app可视化创建mysql关系表

平时自己建表,没有可视化图形参考 为了便于理解,用drwadb画mysql关系表 drawDB | Online database diagram editor and SQL generator

火绒互联网安全软件:自主引擎,精准防御

在数字时代&#xff0c;网络安全是每一个用户都必须重视的问题。无论是个人用户还是企业用户&#xff0c;都需要一款高效、可靠的反病毒软件来保护设备免受恶意软件的侵害。今天&#xff0c;我们要介绍的 火绒互联网安全软件&#xff0c;就是这样一款由资深工程师主导研发并拥有…

【前端基础】8、CSS的选择器

一、什么是选择器&#xff1f; 根据一定的规则选出符合条件的HTML元素&#xff0c;从而为他们添加各种特定的样式。 二、选择器分类 通用选择器元素选择器类选择器id选择器属性选择器后代选择器兄弟选择器选择器组伪类 三、通用选择器&#xff08;*&#xff09; 作用&…

Gitee Team:关键领域行业DevSecOps落地的项目管理引擎

在全球数字化转型浪潮下&#xff0c;关键领域行业的软件研发正面临前所未有的挑战与机遇。国产化进程的加速推进与国防装备的智能化转型&#xff0c;对软件研发效能和质量提出了更高要求。在这样的背景下&#xff0c;Gitee Team作为国内领先的研发协作平台&#xff0c;正在为关…

网址为 http://xxx:xxxx/的网页可能暂时无法连接,或者它已永久性地移动到了新网址

这是由于浏览器默认的非安全端口所导致的&#xff0c;所谓非安全端口&#xff0c;就是浏览器出于安全问题&#xff0c;会禁止一些网络浏览向外的端口。 避免使用6000,6666这样的端口 6000-7000有很多都不行&#xff0c;所以尽量避免使用这个区间 还有在云服务器中&#xff0c…

鸿蒙跨平台开发教程之Uniapp布局基础

前两天的文章内容对uniapp开发鸿蒙应用做了一些详细的介绍&#xff0c;包括配置开发环境和项目结构目录解读&#xff0c;今天我们正式开始写代码。 入门新的开发语言往往从Hello World开始&#xff0c;Uniapp的初始化项目中已经写好了一个简单的demo&#xff0c;这里就不再赘述…

uniapp使用npm下载

uniapp的项目在使用HBuilder X创建时是不会有node_modules文件夹的&#xff0c;如下图所示&#xff1a; 但是uni-app不管基于哪个框架&#xff0c;它内部一定是有node.js的&#xff0c;否则没有办法去实现框架层面的一些东西&#xff0c;只是说它略微有点差异。具体差异表现在…

C# 的异步任务中, 如何暂停, 继续,停止任务

namespace taskTest {using System;using System.Threading;using System.Threading.Tasks;public class MyService{private Task? workTask;private readonly SemaphoreSlim semaphore new SemaphoreSlim(0, 1); // 初始为 0&#xff0c;Start() 启动时手动放行private read…

2025年AI工程师认证深度解析:AAIA认证体系全景指南与实战策略

一、IAAAI认证体系演进与价值定位 1.1 国际人工智能认证发展现状 全球人工智能认证市场呈现显著分化态势。据Gartner 2025Q1报告显示&#xff0c;北美市场以IEEE/ACM双认证体系为主导&#xff08;市占率38%&#xff09;&#xff0c;欧盟区推行AI Act合规认证&#xff08;强制…

统计服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息

文章目录 一、背景二、说明三、页面四、代码 前端 MonitorServiceProcessPage.vueMonitorServiceProcessTable.vueMonitorServiceProcessTableButton.vueaddMonitorTask.vueproductOperation.vueshowMonitorTask.vueMonitorSystemLog.vueMonitorTask.vueMonitorTaskLog.vueReal…