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

打印 上一主题 下一主题

主题 809|帖子 809|积分 2427

❃博主首页 :   「码到三十五」   ,同名公众号 :「码到三十五」,wx号 : 「liwu0213」   
  ☠博主专栏 :   <mysql高手>    <elasticsearch高手>    <源码解读>    <java核心>    <口试攻关>   
  ♝博主的话 :  搬的每块砖,皆为峰峦之基;公众号搜刮「码到三十五」关注这个爱发技能干货的coder,一起筑基   
分布式系统中,分布式锁是一种常用的同步机制,通过MongoDB提供的findAndModify原子操作,可以有用地实现分布式锁的功能。

  
一、MongoDB的锁机制

MongoDB的锁机制主要用于掩护数据的划一性和精确性。当多个客户端同时对同一文档进行操作时,MongoDB通过锁机制来确保每个操作的顺序和结果都是精确的。锁机制通过对文档进行加锁来实现,包罗读锁和写锁。读锁答应多个客户端同时读取文档,但写锁则是互斥的,即同一时间只能有一个客户端持有写锁。
findAndModify是MongoDB提供的一个非常强大的命令,它答应你同时执行查询和更新操作,而且这个操作是原子的。这意味着在findAndModify执行期间,没有其他客户端可以修改被查询的文档,直到该命令完成。这个特性使其成为实现分布式锁的理想选择。
二、分布式锁的需求

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

  • 互斥性:在任意时候,只有一个客户端能够持有锁。
  • 不会死锁:客户端在持有锁期间如果崩溃或断开毗连,锁必须能够被释放,以防止死锁。
  • 容错性:在分布式环境下,部门节点或网络故障不应影响锁的正常工作。
  • 高性能:锁的获取和释放操作应该尽大概快,以淘汰对系统性能的影响。
三、基于MongoDB的分布式锁实现原理

1. 锁集合的创建

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

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


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



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

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


  • 再次利用findAndModify命令查询并更新locks集合中的对应锁文档。
  • 更新操作将文档的持有者设置为空(或某个特定的释放标识),并大概更新锁定时间或重入计数器(如果实现了重入性)。
  • 如果必要,还可以更新过期时间字段以清除过期的锁。
在分布式系统中,实现锁机制是一项关键任务,用于控制对共享资源的访问,防止数据差别等。MongoDB的findAndModify命令是一种强大的原子操作,可以用于实现简朴的分布式锁。下面详细介绍其原理,并在Spring Boot环境中给出一个实现案例。
MongoDB findAndModify原理

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

  • 锁定机制

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

  • 释放机制

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

四、Spring Boot中简朴实现

Spring Boot中可以利用Spring Data MongoDB与MongoDB的交互。
1. 定义锁文档

  1. import org.springframework.data.annotation.Id;
  2. import org.springframework.data.mongodb.core.mapping.Document;
  3. @Document(collection = "locks")
  4. public class Lock {
  5.     @Id
  6.     private String id;
  7.     private String lockName;
  8.     private String holder;
  9.     private Date lockedAt;
  10.     // Getters and Setters
  11. }
复制代码
2. 实现锁服务

  1. import org.springframework.beans.factory.annotation.Autowired;
  2. import org.springframework.data.mongodb.core.FindAndModifyOptions;
  3. import org.springframework.data.mongodb.core.MongoTemplate;
  4. import org.springframework.data.mongodb.core.query.Criteria;
  5. import org.springframework.data.mongodb.core.query.Query;
  6. import org.springframework.data.mongodb.core.query.Update;
  7. import org.springframework.stereotype.Service;
  8. @Service
  9. public class LockService {
  10.     @Autowired
  11.     private MongoTemplate mongoTemplate;
  12.     public boolean tryLock(String lockName, String clientId) {
  13.         Query query = new Query();
  14.         query.addCriteria(Criteria.where("lockName").is(lockName).and("holder").isNull());
  15.         Update update = new Update();
  16.         update.set("holder", clientId);
  17.         update.set("lockedAt", new Date());
  18.         FindAndModifyOptions options = new FindAndModifyOptions();
  19.         options.returnNew(true);
  20.         Lock lock = mongoTemplate.findAndModify(query, update, options, Lock.class);
  21.         return lock != null && lock.getHolder().equals(clientId);
  22.     }
  23.     public void releaseLock(String lockName, String clientId) {
  24.         Query query = new Query();
  25.         query.addCriteria(Criteria.where("lockName").is(lockName).and("holder").is(clientId));
  26.         Update update = new Update();
  27.         update.set("holder", null);
  28.         mongoTemplate.findAndModify(query, update, Lock.class);
  29.     }
  30. }
复制代码
3. 利用锁服务

在服务层或控制器中,注入LockService并调用tryLock和releaseLock方法来控制对共享资源的访问。
五、留意



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

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


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

一给

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表