mongodb的并发优化

MongoDB的锁模式

  1. MongoDB的锁设计
    MongoDB的高性能表现离不开它的多粒度锁机制。多粒度主要可以针对不同层级的数据库对象进行枷锁,通过避免全局性的互斥来提升并发能力。从整个数据库层面看,MongoDB的并发锁的分层如下图所示:
    在这里插入图片描述

    从上往下是一个逐步细分的关系,分别为Global(全局)、Database(数据库)、Collection(集合)、Document(文档)。需要说明的是,mongodb只定义了前三种级别的锁,对于文档级的由WiredTiger引擎实现的,其内部使用了MVCC乐观锁的方式来实现并发控制。因此,并不是所有引擎的实现都支持文档级别的锁。
    除此之外,针对不同的读写方式,数据库将所类型分为以下几种。

    • 读锁(R),代表共享锁(S),允许多个线程同时读取一个集合,读读不互斥。
    • 写锁(W),代表排它锁(X),允许一个线程写入数据,写写互斥,读写互斥。
    • 意向读锁(r),代表意向的共享锁(IS)。
    • 意向写锁(w),代表意向的排它锁(IX)。

    其中,读写锁一般比较容易理解,但是意向锁却有着更为重要的意义。他描述了一种中间状态(非真正的互斥)。对于某一层资源进行锁定时,都需要对其高层次的资源添加意向锁。而且,意向锁之间不是互斥的,这样可以保证在高层次资源上尽可能不会出现锁争用的情况。
    比如,更新users集合中的某一条用户记录(id=1)时,需要经过如下流程:

    1. 对Global添加意向写锁。
    2. 对Database添加意向写锁。
    3. 对Collection添加意向写锁。
    4. 对users(id=1)记录执行更新(乐观锁)。
  2. 查看锁的状态

    db.currentOp()
    
  3. 锁的让步
    在某些情况下,读写操作会让出它们所持有的锁,这个操作是在数据库内部发生的。
    对于一些长时间运行的读写操作,如查询、更新和删除,有很大的概率会产生这种让步行为。对于updateMany这样的操作,mongodb同样会在每次更新文档的间隙让出锁。wiredTiger已经达到了文档级别的细粒度并发控制,对于集合以及以上级别的意向锁也不会影响并发,那为什么还需要进行让渡呢?

    • 避免长时间执行的存储性事务,因为这些会给内存造成较大的压力。
    • 作为中断响应点,以便可以杀死长时间运行的操作。
    • 允许一些关键的排它新操作得到执行,例如索引/集合的创建、删除。

MVCC

在并发场景下,用于保证一致性的做法一般有两种:

  • 使用互斥锁。
  • 基于MVCC的多版本并发控制。

其中,MVCC是目前数据库中使用最广泛的一种机制,包括mysql、mongodb等流行数据库都在使用。我们可以将MVCC看成行级锁的一种妥协,它在许多情况下避免了使用锁,同时可以提供更小开销。相比互斥锁来说,MVCC允许存在数据的多份复制,可以使读操作和写操作同时进行,很大程度上提升了并发能力。
不同数据库对MVCC的实现方式有些不同,Mongodb对于文档的并发控制是由wiredtiger引擎实现的。其对于数据的每一次修改都会在内存中产生一个新的版本,并通过链表结构进行记录。
在这里插入图片描述
访问数据的线程会自动检查是否存在更新的版本并获取最近修改的副本。如果是删除操作,则会写入数据的delete标记,读取时进行判别即可。在这种模式下,允许写入线程在读取线程执行读操作时并发创建新版本,该过程是无锁的。而只有在多个线程尝试更新同一个记录时才会产生写冲突,此时只会有一个更新操作成功,其他操作会在稍后进行重试。
在这里插入图片描述
mongodb在写入更新记录时使用了基于version的乐观锁模式,当写冲突产生(尝试更新失败)时,wiredTiger内部会产生WT_ROLLBACK结果,而mongodb检测到该状态之后会抛出异常,最终由写入的执行线程捕获并重试。
mongodb在一开始就实现了单文档的事务,用来保证文档写操作、oplog以及journal日志的原子性。但这种单文档事务仍然属于内部机制,这是一种隐性的事务。而mongodb4.0版本之后支持的多文档事务则是显性的事务,多文档事务提供了基于快照一致性读能力,这样同样离不开MVCC。

总的来说,mongodb的MVCC在实现上有如下特性。

  • 在内存中存放文档的多个版本。
  • 读取数据行默认获取到当前最新的提交。如果在多文档事务中,还可以使用snapshot级别读取事务一致的版本。
  • 写操作时只会追加新的版本,不会和读操作互斥。
  • 对于同一个文档的并发更新会导致写冲突,由mongodb内部自动进行重试。

原子性操作

在分布式系统中,有很大概率会出现并发读写的情况。为了保证业务数据的一致性状态不遭受破坏,开发者通常需要对潜在的并发以及异常场景做出估量并采取适当的原子性保护。
几乎所有主流的编程语言都提供了良好的并发框架支持,例如,java的concurrent包就提供了全面的锁特性实现。借由这些能力,我们很容易在单进程应用中解决原子性方面的问题。但是,微服务架构让应用程序处理并发原子性问题变得更加复杂,关键在于这些进程内施加的本地锁无法解决分布式的问题
对于mongodb来说,更多的应用实践倾向于利用单文档事务性来解决原子性问题,当然,也可以使用高版本中的多文档事务来实现,但缺点是必须接受多文档事务带来的性能损失。

乐观锁

CAS是避免并发冲突的优选模式,实现乐观锁的前提是文档的原子性更新。借助mongodb的CAS模式,可以巧妙解决许多并发性的问题。同时,可以使用版本号模式来实现CAS,通过在文档中加入一个特殊的版本号字段实现。

缓解行锁竞争

尽管mongodb实现了MVCC的文档级的并发控制,但如果是对于但文档的更新,则仍然会遇到高并发的问题。
一个典型的例子就计数器,这种需求在大量应用中是很常见的。比如用计数器表示缓存一段时间内的用户浏览量。计数器文档的体积很小,通常只有一个key和一个整数型value,而且很容易被缓存和快速读取。但唯一不足的是它的更新会受到行锁互斥的影响,从而导致性能的下降。
例如对一个文档同时执行100个更新操作,会产生100个MVCC版本,但只有一个会被成功保留,剩余的99个将会被重试,接下来继续更新,将又会剩下98个进行重试。。。这个过程会持续到所有操作都成功。因此,对于n个并发更新会产生指数级别的提交任务,这就会导致CPU使用率高居不下。如果希望改善这种情况,则可以采用分槽的做法,将一个文档分成诺干个文档,插入数据的时候随机插入其中一个文档中。这样,我们就将单个文档的写压力分摊到n个文档上,此时的行锁冲突会降低不少!但这样的读取方式需要做一些变动,即查询一天的计数器值需要对n个slot的值进行累计计算。当然,如果查询非常频繁,还可以使用定时器将slot的值预先汇聚到总表读取,进一步提高效率。

避免重复数据

某些重复性的数据会干扰系统的允许,严重的情况下还会导致一些难以修复的错误,所以应避免植入重复的业务数据。在开发层面,我们能做就是为字段添加唯一索引。

那些影响并发的操作

最后,我们来关注一些可能会影响并发性能的命令。这里所指的是,一些管理性质的命令实质上会对数据库集合,甚至整个库产生锁,在不经意间影响正常的业务操作。

  1. 创建索引,createIndex命令可以用于创建多个索引。整个命令的执行需要使用临时内存,这部分内存会由mongodb额外向操作系统申请。一个createIndex命令可用的最大内存默认是500mb,如果超过了这个值就会开始使用磁盘空间交换,所以这里可能会出现性能拐点。在mongodb4.0以及之前的版本中,索引的创建默认会使用前台模式,这会对整个数据库产生一个全局的排它锁,从而阻碍正常的业务。因此,建议创建索引应准确使用background模式。这个问题在mongodb4.2版本中有所改进,索引创建不再区分模式,而是仅仅在创建的一开始和最后产生短暂的排它锁,而且目标对象也改成了当前的集合。对超大集合建立索引的过程可能是缓慢的,这是因为创建索引时需要扫描全部的文档,而且一些常规的业务操作会使得建立索引的工作线程出现让步等待而进一步延缓。考虑到对性能的影响,在副本集执行大集合的索引创建时可以采取滚动操作的方式。
  2. 删除索引,dropIndex命令在执行时同样会产生库级的排他锁,在mongodb4.3版本中调整为集合的排它锁。
  3. 查询全部集合,listCollections会对数据库产生一个意向读锁。在mongodb4.0版本之前,这个命令对数据库产生的是一个共享锁,由于共享锁和意向写锁是互斥的,所以这会影响数据库中数据的写操作。
  4. 重命名集合,renameCollection会产生集合的排它锁,但rename操作的时间一般较短。
  5. 一些全局性的维护操作,如db.copyDatabase,db.collection.reIndex操作会导致所有数据库都被锁住,直到操作完成才能释放锁。

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

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

相关文章

Python 环境管理介绍

pip pip 是 Python 的标准包管理工具&#xff0c;用于安装和管理 Python 软件包。它允许你从 Python 包索引&#xff08;PyPI&#xff09;下载并安装第三方库&#xff0c;并能自动解决依赖问题。 第三方库的安装与卸载 pip install <package>pip uninstall <packag…

oracle apex post接口

日常记录 使用到了apex_json方式接收 、、、1 首先&#xff0c;接口通过body传递过来&#xff0c;成功接收到&#xff0c; 数据格式为 JSON_OBJECT_T l_json : JSON_OBJECT_T.parse(:body); 这里我用参数接收到 然后 里面是包含了 "data" 我用 继续接收到这个 l…

PLC通讯

PPI通讯 是西门子公司专为s7-200系列plc开发的通讯协议。内置于s7-200 CPU中。PPI协议物理上基于RS-485口&#xff0c;通过屏蔽双绞线就可以实现PPI通讯。PPI协议是一种主-从协议。主站设备发送要求到从站设备&#xff0c;从站设备响应&#xff0c;从站不能主动发出信息。主站…

易飞报错:输入的库位并不存在,请重新输入

首先&#xff0c;判断使用的账套是否启用了库位管理&#xff0c;若启用&#xff0c;检查库位信息是否正确&#xff0c; 若没启用&#xff0c;可以进行一下操作处理&#xff1a; 首先在公用参数中启用库位管理&#xff0c; 然后使用取消库位管理进行取消操作。 以上方法基本可以…

綫性與非綫性泛函分析與應用_3.例題-母本

第3章 巴拿赫空間 1. 巴拿赫不動點定理 例題1 問題:在完備度量空間(\mathbb{R},d)(d(x,y)=\vert x - y\vert)中,定義映射f(x)=\frac{1}{2}x + 1,求f的不動點。 解析:首先驗證f是壓縮映射。對於任意x,y\in\mathbb{R},d(f(x),f(y))=\vert(\frac{1}{2}x + 1)-(\frac{1}{2…

go语言闭包的立即执行和不立即执行

在 Go 语言中&#xff0c;闭包&#xff08;closure&#xff09;是一种特殊的函数&#xff0c;它可以访问其定义时所在的作用域中的变量&#xff0c;即使这个函数在其他地方被调用。闭包的“立即执行”和“不立即执行”主要取决于闭包的定义和调用方式。 1. 闭包的定义 闭包是…

Mybatis常用动态 SQL 相关标签

1. <if> 用于条件判断&#xff0c;当满足条件时执行对应的 SQL 片段。 示例: <select id"findUser" resultType"User">SELECT * FROM usersWHERE 11<if test"name ! null and name ! ">AND name #{name}</if><if…

500字理透react的hook闭包问题

在react中hook的闭包问题很容易在不经意间犯错&#xff0c;项目写大了之后更是难以找到到底是哪里出了问题。 为什么会出现闭包问题 出现闭包问题的原因就是函数中操作的变量不是最新的变量&#xff0c;什么意思呢&#xff0c;我们知道函数组件每次刷新都是重新运行一次函数&…

买股票的最佳时机 - 2

买卖股票的最佳时机 III 题目描述&#xff1a; 提示&#xff1a; 1 < prices.length < 1050 < prices[i] < 105 分析过程&#xff1a; 写动态规划&#xff0c;我们需要考虑一下问题&#xff1a; 定义状态状态转移方程初始条件 遍历顺序 4种状态&#xff1a; …

【HarmonyOS Next】地图使用详解(一)

背景 这系列文章主要讲解鸿蒙地图的使用&#xff0c;当前可以免费使用&#xff0c;并提供了丰富的SDK给开发者去自定义控件开发。目前可以实现个性化显示地图、位置搜索和路径规划等功能&#xff0c;轻松完成地图构建工作。需要注意的是&#xff0c;现在测试只能使用实体手机去…

【C++】 时间库chrono计算程序运行时间

C 时间库chrono计算程序运行时间 本文总结了chrono库的引入方法以及计算程序片段运行时间的方法 一、chrono库的引入方法&#xff08;注意事项&#xff09; 首先chrono是属于std命名空间的。 所以在程序中应该这样包含头文件&#xff1a; #include <chrono> using n…

计算机毕业设计SpringBoot+Vue.jst0甘肃非物质文化网站(源码+LW文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

EVM系区块链开发网节点搭建及测试详细文档

文章目录 一. 编写说明1.1 文档说明1.2 配置信息二. docker 方式部署区块链开发网2.1 centos7 部署docker环境2.1.1 卸载旧版本2.1.2 使用 yum 安装2.1.3 使用官方安装脚本自动安装2.1.4 启动并加入开机启动2.2 区块链节点镜像生成2.3 区块链开发网节点容器生成2.3.1 配置文件编…

第4章 4.1 Entity Framework Core概述

4.1.1 什么是ORM ORM (object tralstional mapping ,对象关系映射)中的“对象”指的就是C#中的对象&#xff0c;而“关系”是关系型数据库&#xff0c;“映射”指搭建数据库与C#对象之间的“桥梁”。 比如使用ORM &#xff0c;可以通过创建C#对象的方式把数据插入数据库而不需…

关于Yudao(芋道)和Ruoyi(若依)两个开源框架的发布时间、功能定位以及当前发展情况

根据现有信息&#xff0c;关于Yudao&#xff08;芋道&#xff09;和Ruoyi&#xff08;若依&#xff09;两个开源框架的发布时间、功能定位以及当前发展情况&#xff0c;可以综合如下分析&#xff1a; 一、发布时间与先后顺序 Ruoyi&#xff08;若依&#xff09; Ruoyi框架的公开…

JavaScript实现一个函数,找出数组中重复出现次数最多的元素。

JavaScript实现一个函数&#xff0c;找出数组中重复出现次数最多的元素。 实现思路 要找出数组里重复出现次数最多的元素&#xff0c;咱们可以这么干&#xff1a; 先弄个“小账本”&#xff08;也就是一个对象&#xff09;&#xff0c;用来记录数组里每个元素出现的次数。接…

10. 九转金丹炼矩阵 - 矩阵置零(标记优化)

哪吒在数据修仙界中继续他的修炼之旅。这一次,他来到了一片神秘的金丹谷,谷中有一座巨大的九转金丹炉,炉身闪烁着神秘的光芒。金丹炉的入口处有一块巨大的石碑,上面刻着一行文字:“欲破此炉,需以九转金丹之力,炼矩阵之零,标记优化定乾坤。” 哪吒定睛一看,石碑上还有…

PostgreSQL vs MongoDB:优劣分析及适用场景

PostgreSQL vs MongoDB&#xff1a;优劣分析及适用场景 PostgreSQL 和 MongoDB 是两种非常不同的数据库系统&#xff0c;它们各自具有独特的优势和适用场景。以下是对这两种数据库的优劣分析及适用场景&#xff1a; PostgreSQL 优势&#xff1a; ACID 兼容性&#xff1a;Po…

出行项目案例

spark和kafka主要通过Scala实现&#xff0c;Hadoop和HBase主要基于java实现。 通过该项目&#xff0c;主要达到以下目的&#xff1a; &#xff08;1&#xff09;通用的数据处理流程&#xff0c;入门大数据领域 &#xff08;2&#xff09;真实体验大数据开发工程师的工作 &a…

PyEcharts 数据可视化:从入门到实战

一、PyEcharts 简介 PyEcharts 是基于百度开源可视化库 ECharts 的 Python 数据可视化工具&#xff0c;支持生成交互式的 HTML 格式图表。相较于 Matplotlib 等静态图表库&#xff0c;PyEcharts 具有以下优势&#xff1a; 丰富的图表类型&#xff08;30&#xff09;动态交互功…