SpringBoot中基于MongoDB的findAndModify原子操作实现分布式锁原理详解

news2024/12/28 11:46:20
❃博主首页 : 「码到三十五」 ,同名公众号 :「码到三十五」,wx号 : 「liwu0213」
☠博主专栏 : <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关>
♝博主的话 : 搬的每块砖,皆为峰峦之基;公众号搜索「码到三十五」关注这个爱发技术干货的coder,一起筑基

分布式系统中,分布式锁是一种常用的同步机制,通过MongoDB提供的findAndModify原子操作,可以有效地实现分布式锁的功能。

文章目录

    • 一、MongoDB的锁机制
    • 二、分布式锁的需求
    • 三、基于MongoDB的分布式锁实现原理
      • 1. 锁集合的创建
      • 2. 尝试获取锁
      • 3. 锁的重入和超时
      • 4. 释放锁
      • MongoDB findAndModify原理
    • 四、Spring Boot中简单实现
        • 1. 定义锁文档
        • 2. 实现锁服务
        • 3. 使用锁服务
    • 五、注意

一、MongoDB的锁机制

MongoDB的锁机制主要用于保护数据的一致性和正确性。当多个客户端同时对同一文档进行操作时,MongoDB通过锁机制来确保每个操作的顺序和结果都是正确的。锁机制通过对文档进行加锁来实现,包括读锁和写锁。读锁允许多个客户端同时读取文档,但写锁则是互斥的,即同一时间只能有一个客户端持有写锁。

findAndModify是MongoDB提供的一个非常强大的命令,它允许你同时执行查询和更新操作,并且这个操作是原子的。这意味着在findAndModify执行期间,没有其他客户端可以修改被查询的文档,直到该命令完成。这个特性使其成为实现分布式锁的理想选择。

二、分布式锁的需求

在分布式系统中,分布式锁需要满足以下几个基本要求:

  1. 互斥性:在任意时刻,只有一个客户端能够持有锁。
  2. 不会死锁:客户端在持有锁期间如果崩溃或断开连接,锁必须能够被释放,以防止死锁。
  3. 容错性:在分布式环境下,部分节点或网络故障不应影响锁的正常工作。
  4. 高性能:锁的获取和释放操作应该尽可能快,以减少对系统性能的影响。

三、基于MongoDB的分布式锁实现原理

1. 锁集合的创建

首先,在MongoDB中创建一个专门的集合(如locks)来存储锁信息。每个锁由一个文档表示,文档中包含了锁的关键信息,如锁名(lockName)、持有者(holder)、锁定时间(lockedAt)、过期时间(expiresAt)等。

2. 尝试获取锁

当客户端需要获取锁时,它执行以下步骤:

  • 使用findAndModify命令查询locks集合中的对应锁文档。
  • 查询条件包括锁名和当前持有者为空(表示锁未被占用)且当前时间小于过期时间(如果存在过期时间字段)。
  • 更新操作设置持有者为当前客户端的标识,设置锁定时间,并可选地设置过期时间。
  • 如果findAndModify命令成功更新了文档,则表示客户端成功获取了锁;如果更新失败(因为其他客户端已经设置了持有者或已过期时间已过),则表示锁已被占用或已过期。

3. 锁的重入和超时

  • 重入性:可以通过在文档中增加一个重入计数器来实现锁的重入性。当客户端尝试重新获取已被自己持有的锁时,重入计数器增加。
  • 超时机制:设置过期时间(expiresAt)来防止客户端在持有锁期间崩溃而无法释放锁。当过期时间到达时,其他客户端可以清除该锁(通过检查并更新expiresAtholder字段)。

4. 释放锁

当客户端完成操作后,它执行以下步骤来释放锁:

  • 再次使用findAndModify命令查询并更新locks集合中的对应锁文档。
  • 更新操作将文档的持有者设置为空(或某个特定的释放标识),并可能更新锁定时间或重入计数器(如果实现了重入性)。
  • 如果需要,还可以更新过期时间字段以清除过期的锁。

在分布式系统中,实现锁机制是一项关键任务,用于控制对共享资源的访问,防止数据不一致。MongoDB的findAndModify命令是一种强大的原子操作,可以用于实现简单的分布式锁。下面详细介绍其原理,并在Spring Boot环境中给出一个实现案例。

MongoDB findAndModify原理

findAndModify是MongoDB中的一个命令,它用于查找并更新一个文档,这个操作是原子的,意味着在查找和更新文档期间,不会有其他操作可以修改这个文档。利用这个特性,我们可以创建一个简单的分布式锁:

  1. 锁定机制

    • 在数据库中创建一个集合(例如locks),每个锁由一个文档表示。
    • 每个文档包含锁的关键信息,如锁名(lockName)、持有者(holder)、锁定时间(lockedAt)等。
    • 当需要锁定某个资源时,使用findAndModify尝试更新集合中的一个文档,设置holderlockedAt。如果更新成功,则表示获得了锁;如果失败(例如,因为其他客户端已经设置了holder),则表示锁已被占用。
  2. 释放机制

    • 持有锁的客户端在完成操作后,需要释放锁。这通常通过另一个findAndModify操作来完成,将文档的holder设置为null或某个特定的释放标识。

四、Spring Boot中简单实现

Spring Boot中可以使用Spring Data MongoDB与MongoDB的交互。

1. 定义锁文档
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "locks")
public class Lock {
    @Id
    private String id;
    private String lockName;
    private String holder;
    private Date lockedAt;

    // Getters and Setters
}
2. 实现锁服务
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

@Service
public class LockService {

    @Autowired
    private MongoTemplate mongoTemplate;

    public boolean tryLock(String lockName, String clientId) {
        Query query = new Query();
        query.addCriteria(Criteria.where("lockName").is(lockName).and("holder").isNull());

        Update update = new Update();
        update.set("holder", clientId);
        update.set("lockedAt", new Date());

        FindAndModifyOptions options = new FindAndModifyOptions();
        options.returnNew(true);

        Lock lock = mongoTemplate.findAndModify(query, update, options, Lock.class);
        return lock != null && lock.getHolder().equals(clientId);
    }

    public void releaseLock(String lockName, String clientId) {
        Query query = new Query();
        query.addCriteria(Criteria.where("lockName").is(lockName).and("holder").is(clientId));

        Update update = new Update();
        update.set("holder", null);

        mongoTemplate.findAndModify(query, update, Lock.class);
    }
}
3. 使用锁服务

在服务层或控制器中,注入LockService并调用tryLockreleaseLock方法来控制对共享资源的访问。

五、注意

  • 锁的粒度:根据实际需求选择合适的锁粒度,避免过细或过粗的锁粒度导致的性能问题或资源竞争。
  • 锁的过期时间:合理设置锁的过期时间,以确保在客户端崩溃或其他异常情况下能够释放锁。
  • 网络延迟和分区:在分布式系统中,网络延迟和分区问题可能会导致findAndModify操作的延迟或失败。需要考虑这些因素对锁的性能和可靠性的影响。

关注公众号[码到三十五]获取更多技术干货 !

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

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

相关文章

html简单注册页面

简单的html注册页面&#xff0c;代码如下&#xff0c;效果如下 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">&l…

Linux | 匿名管道和命名管道:进程间通信数据流的桥梁

目录 1、进程间通信目的 2、管道——匿名管道和命名管道 匿名管道 匿名管道的示例代码&#xff1a;将数据写入管道、子进程从管道读取数据并将其输出到bash中 父子进程通过匿名管道建立通信 重点&#xff1a;管道的五个特点 命名管道&#xff08;也称为FIFO&#xff09;…

Java项目:128 基于Spring Boot的装饰工程管理系统

作者主页&#xff1a;源码空间codegym 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 本系统包含管理员、员工和客户角色 管理员权限操作的功能包括管理合同信息&#xff0c;管理合同报价&#xff0c;管理立项项目&#xff0c;管…

机器学习:opencv--图像边缘检测

目录 前言 一、图像边缘检测 1.边缘检测 2.边缘检测的方法 二、Sobel算子 1.Sobel算子 2.计算 3.代码实现 4.代码步骤解析 1.导入图片 2.处理x轴和y轴的边缘并相加 三、Scharr算子 1.Scharr算子 2.计算 3.代码实现 四、Laplacian算子 1.Lapla…

本地部署训练、测试controlnet的完整过程(包括报错和代码)

文章目录 参考内容一、训练流程下载相关文件① 需要下载的权重文件② 下载数据集 二、训练代码两种训练方式&#xff1a;①采用.sh文件②常规的训练 测试代码生成的结果 四、报错NVIDIA的驱动太老需要更新生成出全黑图 参考内容 diffusers库提供的官方训练代码 利用到的fill50…

Vue(六) render函数、Vue.config.js配置文件,ref属性,props配置项、mixin混入、插件、scoped

文章目录 一、render函数二、Vue.config.js配置文件三. ref属性四. props配置项五. mixin混入1. 局部混入2. 全局混入 六. 插件七. scoped 一、render函数 在main.js文件中&#xff0c;采用了render函数。 import App from ./App.vuenew Vue({// 这句代码的意思是将App组件放…

Springboot整合J2cache实现声明式缓存方案

Springboot整合J2Cache 一、J2Caceh多级缓存 ​ J2Cache 是 OSChina 目前正在使用的两级缓存框架&#xff08;要求至少 Java 8&#xff09;。 ​ 第一级缓存使用内存(同时支持 Ehcache 2.x、Ehcache 3.x 和 Caffeine)&#xff0c;第二级缓存使用 Redis(推荐)/Memcached 。 L…

【生日视频制作】红色跑车法拉利提车交车仪式感广告展示牌AE模板修改文字软件生成器教程特效素材【AE模板】

生日视频制作教程红色跑车法拉利提车交车仪式感广告展示牌AE模板修改文字特效广软件告生成神器素材祝福玩法AE模板工程 怎么如何做的【生日视频制作】红色跑车法拉利提车交车仪式感广告展示牌AE模板修改文字软件生成器教程特效素材【AE模板】 生日视频制作步骤&#xff1a; 安…

Java小项目IDEA怎么打成jar包

使用IDEA打jar包 在file选项中找 打开jar包所在位置&#xff1a; 将jar包拿出来 直接点击jar包就可以运行

golang关于slice map函数传参的小问题

问题 函数传参了一个slice&#xff0c;在函数内触发了对长度的修改&#xff08;添加或删除&#xff09;&#xff0c;但是未影响函数外的实参由此产生了另一个问题&#xff0c;我们用map在函数内修改会不会有影响不到实参的情况&#xff1f; 结论 map作为函数参数时是引用传递…

go语言并发编程-超详细mutex解析

文章目录 1 go语言并发编程学习-mutex1.1 学习过程1.2 如何解决资源并发访问的问题&#xff1f;【基本用法】1.2.1 并发访问带来的问题1.2.1.1 导致问题的原因 1.2.2 race detector检查data race1.2.3 mutex的基本实现机制以及使用方法1.2.3.1 具体使用-11.2.3.1 具体使用-2 1 …

@Tanstack/vue-query 的使用介绍

Tanstack/vue-query 的使用介绍 前言 在今年的vue conf 会议上&#xff0c;提到了vue-query这个库&#xff0c;这里对它的基本使用做一个介绍。 会议资料地址&#xff1a; https://vueconf.cn/ Tanstack-query的前身是react-query&#xff0c;是一个本地的服务端状态管理的库…

deepin 23 下如何运行绝大数 Windows 游戏?

查看原文 最近有很多 deepiner 一直在询问&#xff1a; “deepin 真的可以玩游戏&#xff1f;” “如何在 deepin 上玩游戏&#xff1f;能玩 windows 上的游戏吗&#xff1f;” 答案是肯定的 “必须能&#xff01;”&#xff0c;下文和大家分享一下在 deepin 上玩游戏实现的…

使用IoC容器--Ninject

Ninject Ninject是一个较新的开源的IoC容器。这是简单和可扩展的。你可以从下面的位置下载IoC容器。 Ninject 或者您可以使用 NuGet 向您的项目添加Ninject。让我们从NuGet向我们的项目中添加Ninject。只需转到您的项目引用并右键单击&#xff0c;然后 ManageNuGet Packages&a…

Java基础 2. Java基础语法

Java基础 2. Java基础语法 文章目录 Java基础 2. Java基础语法2.1. 标识符2.1.1. 标识符的命名规则 :2.1.2. 标识符的命名规范: 2.2. 关键字2.3. 字面量2.3.1. Java中有哪些字面量2.3.2. 加号运算符 2.4. 变量2.5. 二进制2.6. 八进制与十六进制2.7. 原码反码补码2.7.1. byte与b…

每日OJ_牛客_字符串计数(模拟26进制)

目录 牛客_分解因数&#xff08;简单模拟&#xff09; 解析代码 牛客_分解因数&#xff08;简单模拟&#xff09; 字符串计数_美团笔试题_牛客网 解析代码 题目意思&#xff1a;按照字典序列&#xff1a;找到s1和s2之间长度在len1和len2范围内的字符串个数。直接做不好处理&…

UE4 使用AndroidGameDevelopmentExtension(AGDE)对安卓客户端做“断点调试”与“代码热更”

本文的目的 主要介绍了如何通过AndroidGameDevelopmentExtension工具、Visual Studio 2022来进行安卓包调试。 流程全过程 1、安装JDK、Gradle、Android NDK、Android SDK等环境如下&#xff0c;请自行前往官网下载&#xff0c;并确认环境参数配置正确。 2、安装 AndroidGame…

黑悟空上线即登顶,如何一路火遍全网?哪些营销方式值得关注?

几乎人人都有一个大圣梦&#xff0c;就像每个品牌也都想成为“黑神话&#xff1a;悟空”&#xff08;以下简称“黑悟空”&#xff09;。 作为国内首款3A游戏&#xff0c;黑悟空上线即登顶&#xff0c;直接刷爆全网。虽然目前热度渐歇&#xff0c;但创造的流量神话仍被津津乐道。…

大佬借助ChatGPT写论文发刊到手软,四个步骤20个顶级学术提示词指令

大家好,感谢关注。我是七哥,一个在高校里不务正业,折腾学术科研AI实操的学术人。关于使用ChatGPT等AI学术科研的相关问题可以和作者七哥(yida985)交流,多多交流,相互成就,共同进步,为大家带来最酷最有效的智能AI学术科研写作攻略。经过数月爆肝,终于完成学术AI使用教…

Mybatis【分页插件,缓存,一级缓存,二级缓存,常见缓存面试题】

文章目录 MyBatis缓存分页延迟加载和立即加载什么是立即加载&#xff1f;什么是延迟加载&#xff1f;延迟加载/懒加载的配置 缓存什么是缓存&#xff1f;缓存的术语什么是MyBatis 缓存&#xff1f;缓存的适用性缓存的分类一级缓存引入案例一级缓存的配置一级缓存的工作流程一级…