当前位置: 首页 > news >正文

分布式锁原理及实现

目录

一、锁的使用场景

二、如何实现控制?

三、单台服务器使用锁的场景

四、分布式锁

五、Redis 实现分布式锁及存在问题

六、Redisson 实现分布式锁

七、定时任务+锁


一、锁的使用场景

1. 控制定时任务执行

  • 定时任务多次执行浪费资源:多台服务器到同一时间都执行缓存预热
  • 脏数据:多台服务器重复插入数据

2. 买票场景

  • 只有一百张票,用户买票时判断剩余票的数量,还有剩余执行票数减一的操作
  • 只剩一张票了,此时多个用户来买票,判断票都是有剩余,但是第一个用户买完之后就没票了,其他用户也执行了票数减一的操作,出现超卖现象

3. 需求:控制定时任务 / 需要加锁的任务在同一时间只能有一台服务器执行

二、如何实现控制?

以定时任务为例

1. 分离定时任务程序和主程序,只在一台服务器运行定时任务=>成本太大

2. 写死配置,每个服务器都执行定时任务,但是只有 IP 符合配置的服务器才真实执行业务逻辑,其他的直接返回。成本最低;但是我们的 IP 可能是不固定的,把 IP 写的太死了

3. 动态配置,配置是可以轻松的、很方便地更新的(代码无需重启,项目无需重新部署),但是只有 IP 符合配置的服务器才真实执行业务逻辑。

  • 读取数据库中的配置

  • Redis

  • 配置中心(Nacos、Apollo、Spring Cloud Config)

  • 问题:服务器多了、IP 不可控还是很麻烦,还是要人工修改

4. 分布式锁:只有抢到锁的服务器才能执行业务逻辑

  • 坏处:增加成本
  • 好处:不用手动配置,多少个服务器都一样

三、单台服务器使用锁的场景

1. Java 实现同步锁:synchronized 关键字

2. 锁存在 JVM 中,每台 JVM 独立,不共享锁,多机部署锁会失效(多个线程都会获取到不同 JVM 中的同一名称的锁)

3. 单机就会存在单点故障

四、分布式锁

1. 为什么需要分布式锁?

  • 普通锁的缺点:JVM 机分配的锁在多台 Tomcat 中不共享,锁只对单个服务器有效
  • 加锁的重要性:资源有限 / 特定情况下只能有有限 / 唯一的线程获取到锁,执行操作
  • 分布式锁:多进程可见且并且互斥的锁

2. 如何实现分布式锁?

实现分布式锁的核心思想 / 怎么保证同一时间只有一台服务器能抢到锁?

  • 先来的人先把数据改成自己的标识(服务器 IP),后来的人发现标识已存在,就抢锁失败,继续等待
  • 等先来的人执行方法结束,把标识清空(释放锁),其他的人继续抢锁
  • MySQL 数据库:select for update 行级锁(最简单)
  • 乐观锁(实际上没有加锁):乐观锁认为线程安全问题只在少数情况下会发生,所以只要在数据更新时判断是否有其他线程修改了数据
  • Redis 实现互斥锁:基于内存,读写速度快
    • set nx ex:原子性、设置过期时间
    • lua 脚本:保证多条语句的原子性
  • Zookeeper

五、Redis 实现分布式锁及存在问题(误删锁)

1. set nx ex

2. 释放锁

  • 手动释放:del lock
  • 意外:服务器宕机,手动释放锁还未执行
  • 优化:设置过期时间,若未手动释放则等到过期时间到了就会自动释放锁

3. 误删锁

  • 线程 A 在执行时阻塞,过了锁的过期时间,锁自动释放
  • 线程 B 尝试获取锁,获取成功,执行业务
  • A 阻塞之后继续执行,执行结束,释放当前正在被线程 B 占有的锁
  • 线程 C 尝试获取锁,获取成功,执行业务
  • 出现线程 B 和线程 C 并发执行的情况

4. 解决误删锁

  • 判断当前锁的占有线程是不是本线程
  • 如果不是自己占有的锁,就不去释放(别人的锁)

5. 改进锁之后仍然存在问题:判断锁和释放锁的原子性问题

  • 判断锁时是自己正在占有锁
  • 判断锁标识后,执行释放锁之前,线程出现了阻塞,锁到了过期时间,自动释放
  • 其他线程尝试获取锁,获取成功
  • 阻塞之后执行释放锁,还是把别人的锁给释放了
  • 需要保证判断锁和释放锁操作的原子性:Lua 脚本

六、Redisson 实现分布式锁

Github:https://github.com/redisson/redisson

官网:Redisson: Easy Redis Java client with features of In-Memory Data Grid

1. 定义

  • Redisson 是一个在 Redis 基础上实现的 Java 驻内存数据网格
  • 提供了一系列分布式的 Java 常用对象,还提供了许多分布式服务(各种分布式锁的实现)

2. 自己编写 Redisson 的配置,创建 RedissonClient

  • 不推荐使用 spring-boot-starter 整合的 Redisson,版本迭代较快,容易发生冲突
  • 创建 config 对象,添加 Redis 配置:读取 application.yml 中的配置信息
  • 创建 Redisson 实例,返回 Redisson 客户端实例
/*** Redisson 配置* @author 乐小鑫* @version 1.0* @Date 2024-01-21-15:44*/
@Configuration
@ConfigurationProperties(prefix = "spring.redis")
@Data
public class RedissonConfig {private String host;private String port;private String password;@Beanpublic RedissonClient getRedissonClient() {// 1. 创建配置Config config = new Config();String redisAddress = String.format("redis://%s:%s", host, port);config.useSingleServer().setAddress(redisAddress).setPassword(password).setDatabase(3);// 2. 创建 Redisson 客户端实例并返回RedissonClient redisson = Redisson.create(config);return redisson;}
}

3. 测试 Redisson 的功能实现

/*** @author 乐小鑫* @version 1.0* @Date 2024-01-21-15:53*/
@SpringBootTest
public class RedissonTest {@Resourceprivate RedissonClient redissonClient;@Testvoid test() {// listList<String> list = new ArrayList<>();list.add("ghost");System.out.println("List:" + list.get(0));RList<Object> rList = redissonClient.getList("test-list");rList.add("ghost");System.out.println("rList:" + rList.get(0));}
}

4. 看门狗机制的原理

  • 监听当前线程,当前线程没有执行结束就每十秒续期一次
  • 如果线程挂了(注意 Debug 模式时断点过久也会被当成服务器宕机来处理),看门狗机制失效,则不会续期
  • 参考文章:Redisson 分布式锁的watch dog自动续期机制_redisson续期-CSDN博客

七、定时任务+锁

1. getLock():获取 Redisson 的锁对象,需要指定锁的名称

2. tryLock():尝试获取锁(分布式锁),获取成功返回 true,可以指定重试获取锁的等待时间和锁的释放时间

  • waitTime 设置为 0:尝试获取锁获取失败,等待时间为 0,直接放弃获取锁(只尝试一次),因为这里是用户推荐列表的缓存预热定时任务,如果获取锁失败,说明已经有服务器去执行定时任务了,只要执行一次就好了,所以不用再去尝试获取锁

3. unlock():释放锁,放到 finally 语句块中执行,如果 try 语句块中的内容出现异常,也会释放锁,避免发生死锁的情况

/*** 缓存预热定时任务* @author 乐小鑫* @version 1.0*/
@Component
@Slf4j
public class PreCacheUser {@Resourceprivate RedisTemplate redisTemplate;@Resourceprivate UserService userService;@Resourceprivate RedissonClient redissonClient;List<Long> mainUserList = Arrays.asList(3L);// 重要用户列表,为该列表的用户开启缓存预热@Scheduled(cron = "0 59 21 ? * * ")// 每天 21:59 执行定时任务进行用户数据缓存预热public void doPreCacheUser() {// 获取锁对象RLock lock = redissonClient.getLock("langhua:precachejob:doprecache:lock");try {if (lock.tryLock(0,30000L,TimeUnit.MILLISECONDS)) {log.info("get redisson lock" + Thread.currentThread().getId());// 查出用户存到 Redis 中for (Long userId : mainUserList) {QueryWrapper<User> queryWrapper = new QueryWrapper<>();Page<User> userPage = userService.page(new Page<>(1, 20), queryWrapper);// 查询所有用户String key = String.format("langhua:user:recommend:%s", userId);ValueOperations valueOperations = redisTemplate.opsForValue();// 将查询出来的数据写入缓存try {valueOperations.set(key,userPage,24, TimeUnit.HOURS);} catch (Exception e) {log.error("redis key set error", e);}}}} catch (InterruptedException e) {log.error("redisson precache user error", e);} finally {// 释放锁log.info("redisson unlock" + Thread.currentThread().getId());lock.unlock();}}
}

相关文章:

分布式锁原理及实现

目录 一、锁的使用场景 二、如何实现控制&#xff1f; 三、单台服务器使用锁的场景 四、分布式锁 五、Redis 实现分布式锁及存在问题 六、Redisson 实现分布式锁 七、定时任务&#xff0b;锁 一、锁的使用场景 1. 控制定时任务执行 定时任务多次执行浪费资源&#xff…...

蓝桥杯官网填空题(海盗与金币)

题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 12名海盗在一个小岛上发现了大量的金币&#xff0c;后统计一共有将近5万枚。 登上小岛是在夜里&#xff0c;天气又不好。由于各种原因&#xff0c;有的海盗偷拿了很…...

JavaScript 中JSON 字符串和对象之间的转换。

JSON.stringify() 方法&#xff08;对象转换为 JSON 字符串&#xff09; 用于将 JavaScript 对象转换为 JSON 字符串。 它接受一个 JavaScript 对象作为参数&#xff0c;并返回对应的 JSON 字符串表示。例如&#xff1a; const obj { name: John, age: 25 }; const jsonStr…...

All the stories begin at installation

Before installation, there are some key points about Conan: “Conan is a dependency and package manager for C and C languages.”“With full binary management, Conan can create and reuse any number of different binaries (for different configurations like a…...

Linux文件系统与设备文件

Linux文件系统与设备文件 文章目录 Linux文件系统与设备文件Linux文件操作文件操作系统调用C库文件操作 Linux文件系统Linux文件系统目录结构Linux文件系统与设备驱动file结构体inode结构体file结构体和inode结构体的区别 devfsudev用户空间设备管理sysfs文件系统与Linux设备模…...

QT的绘图系统QPainterDevice与文件系统QIODevice

QT的绘图系统&#xff08;QPainterDevice&#xff09;与文件系统&#xff08;QIODevice&#xff09; 文章目录 1、Qt 的绘图系统1、QPainter的使用2、QPen(画笔&#xff09;及QBursh&#xff08;画刷&#xff09;3、手动更新窗口4、绘图设备1、四种绘图设备的 区别2、 QBitmap3…...

Spark流式读取文件数据

流式读取文件数据 from pyspark.sql import SparkSession ss SparkSession.builder.getOrCreate() # todo 注意1&#xff1a;流式读取目录下的文件 --》一定一定要是目录&#xff0c;不是具体的文件&#xff0c;# 目录下产生新文件会进行读取# todo 注意点2&#xff1…...

Leetcode 3011. Find if Array Can Be Sorted

Leetcode 3011. Find if Array Can Be Sorted 1. 解题思路2. 代码实现 题目链接&#xff1a;3011. Find if Array Can Be Sorted 1. 解题思路 这一题挺简单的&#xff0c;就是一个分组进行排序考察&#xff0c;我们将相邻且bit set相同的元素划归到同一组&#xff0c;然后进…...

Databend 开源周报第 129 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 支持标准流 标…...

python 正则表达式学习(1)

正则表达式是一个特殊的字符序列&#xff0c;它能帮助你方便的检查一个字符串是否与某种模式匹配。 1. 特殊符号 1.1 符号含义 模式描述^匹配字符串的开头$匹配字符串的末尾.匹配任意字符&#xff0c;除了换行符&#xff0c;当re.DOTALL标记被指定时&#xff0c;则可以匹配包…...

安全防御-基础认知

目录 安全风险能见度不足&#xff1a; 常见的网络安全术语 &#xff1a; 常见安全风险 网络的基本攻击模式&#xff1a; 病毒分类&#xff1a; 病毒的特征&#xff1a; 常见病毒&#xff1a; 信息安全的五要素&#xff1a; 信息安全的五要素案例 网络空间&#xff1a…...

各省税收收入、个人和企业所得税数据,Shp、excel格式,2000-2021年

基本信息. 数据名称: 各省税收收入、个人和企业所得税数据 数据格式: Shp、excel 数据时间: 2000-2021年 数据几何类型: 面 数据坐标系: WGS84 数据来源&#xff1a;网络公开数据 数据字段&#xff1a; 序号字段名称字段说明1sssr_2021税收收入&#xff08;亿元&am…...

Vue记录

vue2、vue3记录&#xff0c;参考地址&#xff1a;尚硅谷Vue项目实战硅谷甄选&#xff0c;vue3项目TypeScript前端项目一套通关_哔哩哔哩_bilibili vue2记录 经典vue2结构 index.vue&#xff1a; <template><div>...</div> </template><script>…...

【JavaEE进阶】 Spring Boot⽇志

文章目录 &#x1f38b;关于日志&#x1f6a9;为什么要学习⽇志&#x1f6a9;⽇志的⽤途&#x1f6a9;日志的简单使用 &#x1f384;打印⽇志&#x1f6a9;程序中得到⽇志对象&#x1f6a9;使⽤⽇志对象打印⽇志 &#x1f38d;⽇志格式的说明&#x1f6a9;⽇志级别的作用&#…...

《GitHub Copilot 操作指南》课程介绍

第1节&#xff1a;GitHub Copilot 概述 一、什么是 GitHub Copilot 什么是 GitHub Copilot GitHub Copilot是GitHub与OpenAI合作开发的编程助手工具&#xff0c;利用机器学习模型生成代码建议。它集成在开发者的集成开发环境&#xff08;IDE&#xff09;中&#xff0c;可以根…...

结构体(C语言)

结构体 1.结构体基础知识: //结构是一些值的集合,这些值称为成员变量. // 结构的每个成员可以是不同类型的变量. 2.结构的定义 struct peo { char name[10];//姓名 char tele[12];//电话 char gender[5];//性别 int high;//身高 }; struct stu { struct…...

HNU-数据挖掘-实验1-实验平台及环境安装

数据挖掘课程实验实验1 实验平台及环境安装 计科210X 甘晴void 202108010XXX 文章目录 数据挖掘课程实验<br>实验1 实验平台及环境安装实验背景实验目标实验步骤1.安装虚拟机和Linux平台&#xff0c;熟悉Ubuntu环境。2.在Linux平台上搭建Python平台&#xff0c;并安装…...

JavaEE中的监听器的作用和工作原理

在JavaEE&#xff08;Java Platform, Enterprise Edition&#xff09;中&#xff0c;监听器&#xff08;Listener&#xff09;是一种重要的组件&#xff0c;用于监听和响应Web应用程序中的事件。监听器的作用是在特定的事件发生时执行一些自定义的逻辑。常见的监听器包括Servle…...

Webpack5入门到原理1:前言

为什么需要打包工具&#xff1f; 开发时&#xff0c;我们会使用框架&#xff08;React、Vue&#xff09;&#xff0c;ES6 模块化语法&#xff0c;Less/Sass 等 css 预处理器等语法进行开发。 这样的代码要想在浏览器运行必须经过编译成浏览器能识别的 JS、Css 等语法&#xf…...

#vue3 实现前端下载excel文件模板功能

一、需求&#xff1a; 前端无需通过后端接口&#xff0c;即可实现模板下载功能。 通过构造一个 JSON 对象&#xff0c;使用前端常用的第三方库 xlsx&#xff0c;可以直接将该 JSON 对象转换成 Excel 文件&#xff0c;让用户下载模板 二、效果&#xff1a; 三、源码如下&…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库&#xff08;uthash库&#xff09;提供对哈希表的操作&#xff0c;文章如下&#xff1a; C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读&#xff0c;综合法规核心要求、最新修正及企业合规要点&#xff1a; 一、法规背景与目标 生效与强制时间 发布于2023年8月31日&#xff08;OJ公报&…...