在本页面
MongoDB允许多个客户端读取和写入相同的数据。为了确保一致性,它使用锁定和其他 并发控制措施来防止多个客户端同时修改同一数据。这些机制共同保证了对单个文档的所有写入全部发生或完全不发生,并且客户永远不会看到不一致的数据视图。
MongoDB使用多粒度锁定[1],它允许操作锁定在全局,数据库或集合级别,并允许各个存储引擎在集合级别以下(例如,在WiredTiger中的文档级别)实现自己的并发控制。 。
MongoDB使用读取器-写入器锁,允许并发的读取器共享对资源(例如数据库或集合)的访问。
除了用于读取的共享(S)锁定模式和用于写操作的互斥(X)锁定模式之外,意图共享(IS)和意图互斥(IX)模式还表示使用更精细的粒度锁来读取或写入资源的意图。 。当以一定的粒度锁定时,所有更高级别的锁定都使用意图锁来锁定。
例如,当锁定用于写的集合(使用模式X)时,必须将所有数据库锁和全局锁都锁定为意图排他(IX)模式。可以在IS和IX模式下同时锁定单个数据库,但是独占(X)锁不能与任何其他模式共存,共享(S)锁只能与意图共享(IS)锁共存。
锁是公平的,读取和写入按顺序排队。但是,为了优化吞吐量,当一个请求被授予时,所有其他兼容请求将同时被授予,从而有可能在冲突请求之前释放它们。例如,考虑一种情况,其中刚刚释放了X锁,并且冲突队列包含以下各项:
IS→IS→X→X→S→IS
在严格的先进先出(FIFO)顺序中,仅前两种IS模式将被授予。相反,MongoDB实际上将授予所有IS和S模式,并且一旦它们全部耗尽,它将同时授予X,即使在此期间已将新的IS或S请求排队。由于授予将始终将所有其他请求移到队列中,因此任何请求都不会饿死。
在db.serverStatus()
和db.currentOp()
输出中,锁定模式表示如下:
锁定模式 | 描述 |
---|---|
R |
表示共享(S)锁。 |
W |
表示排他(X)锁。 |
r |
表示意图共享(IS)锁。 |
w |
表示Intent Exclusive(IX)锁。 |
[1] | 有关更多信息,请参见Wikipedia页面上的 多个粒度锁定。 |
对于大多数读取和写入操作,WiredTiger使用乐观并发控制。WiredTiger仅在全局,数据库和集合级别使用意图锁。当存储引擎检测到两个操作之间存在冲突时,将引发写冲突,从而导致MongoDB透明地重试该操作。
一些全局操作(通常是涉及多个数据库的短暂操作)仍然需要全局“实例范围”锁。某些其他操作(例如collMod
)仍需要排他数据库锁定。
mongod
实例上的锁状态?¶要报告有关锁的锁利用率信息,请使用以下任何一种方法:
db.serverStatus()
,db.currentOp()
,具体而言,serverStatus输出中的locks
文档或当前操作报告中的字段
可提供有关实例中锁的类型和锁争用量的信息。locks
mongod
在db.serverStatus()
和db.currentOp()
输出中,锁定模式表示如下:
锁定模式 | 描述 |
---|---|
R |
表示共享(S)锁。 |
W |
表示排他(X)锁。 |
r |
表示意图共享(IS)锁。 |
w |
表示Intent Exclusive(IX)锁。 |
要终止操作,请使用db.killOp()
。
在某些情况下,读和写操作可以产生其锁。
在许多情况下,长时间运行的读写操作(例如查询,更新和删除)会产生。MongoDB的操作也可以产生单个文档的修改之间产生锁定在影响多个文档像写操作
update()
与multi
参数。
对于支持文档级并发控制的存储引擎(例如WiredTiger),在访问存储时不需要屈服,因为在全局,数据库和集合级别持有的意图锁不会阻塞其他读取器和写入器。但是,操作将定期产生,例如:
下表列出了一些操作以及它们用于文档级锁定存储引擎的锁定类型:
运作方式 | 数据库 | 采集 |
---|---|---|
发出查询 | r (意图共享) |
r (意图共享) |
插入资料 | w (意图专用) |
w (意图专用) |
删除资料 | w (意图专用) |
w (意图专用) |
更新数据 | w (意图专用) |
w (意图专用) |
执行汇总 | r (意图共享) |
r (意图共享) |
创建索引(前景) | W (独家) |
|
创建索引(背景) | w (意图专用) |
w (意图专用) |
列出收藏 |
在版本4.0中更改。 |
|
映射减少 | W (独家)和R (共享) |
w (意图专用)和r (意图共享) |
某些管理命令可以排他性地锁定数据库较长时间。在某些部署中,对于大型数据库,您可以考虑使mongod
实例脱机,以便不影响客户端。例如,如果a mongod
是副本集的一部分,则使mongod
脱机并在维护进行时让集合服务的其他成员加载。
以下管理操作需要长时间在数据库级别上以独占方式锁定:
运作方式 | |
---|---|
cloneCollectionAsCapped |
|
collMod |
|
compact |
|
convertToCapped |
|
在版本4.2中进行了更改。 如果在同一数据库中重命名集合,则该操作将对源集合和目标集合进行排他(W)锁定。在MongoDB 4.2之前的版本中,在同一数据库中重命名时,该操作将对数据库进行排他(W)锁定。 (仅)如果目标名称空间与源集合位于不同的数据库中,则锁定行为取决于版本:
|
以下管理操作将锁定数据库,但仅在很短的时间内保持锁定:
运作方式 | |
---|---|
也可以看看
在版本4.2中进行了更改。
以下管理操作需要在集合级别具有互斥锁定:
运作方式 | |
---|---|
在版本4.2中进行了更改。 如果在同一数据库中重命名集合,则该操作将对源集合和目标集合进行排他(W)锁定。在MongoDB 4.2之前的版本中,在同一数据库中重命名时,该操作将对数据库进行排他(W)锁定。 (仅)如果目标名称空间与源集合位于不同的数据库中,则锁定行为取决于版本:
|
|
在版本4.2中进行了更改。
|
|
replSetResizeOplog |
在版本4.2中进行了更改。
|
在此之前的MongoDB 4.2,这些操作了对数据库的排他锁,阻止所有数据库操作和它的汇集,直到操作完成。
以下MongoDB操作可能会在多个数据库上获得并持有锁:
运作方式 | |
---|---|
db.copyDatabase() |
此操作获得全局(W)排他锁,并阻止其他操作,直到完成为止。 |
在版本4.2中进行了更改。 对于4.0.0至4.2.1的MongoDB,这些操作采用全局排他(W)锁定并阻止其他操作,直到完成。 从MongoDB 4.2.2开始,这些操作仅获得排他(W)集合锁,而不是全局排他锁。 在MongoDB 4.0之前,这些操作获得了独占(W)数据库锁。 |
|
renameCollection |
在版本4.2中进行了更改。 对于MongoDB 4.2.1和更早版本,此操作在重命名数据库之间的集合时会获得全局排他(W)锁,并阻止其他操作直到完成。 从MongoDB 4.2.2开始,此操作仅在目标数据库上获得排他(W)锁,在源数据库上获得意图共享(r)锁,在源集合上获得共享(S)锁,而不是全局排他锁。 |
在版本4.2中进行了更改。 对于MongoDB 4.2.1和更早版本,此操作获得全局排他(W)锁并阻止其他操作,直到完成。 从MongoDB 4.2.2开始,此操作仅在 |
分片由分布在多个集合提高并发mongod
的情况下,允许碎片服务器(即mongos
进程)来执行任何数量的操作的同时向所述各种下游mongod
实例。
在分片群集中,锁适用于每个单独的分片,而不适用于整个群集。也就是说,每个mongod
实例都独立于分片群集中的其他实例,并使用自己的
锁。一个
mongod
实例上的操作不会阻止任何其他实例上的操作。
对于副本集,当MongoDB写入主数据库上的集合时
,MongoDB还将写入主数据库的oplog,这是local
数据库中的特殊集合。因此,MongoDB必须同时锁定集合的数据库和local
数据库。在mongod
必须同时保持数据库保持一致,并确保写入操作,即使复制锁定两个数据库,分别是“全有或全无”的操作。
在复制中,MongoDB不会将写入顺序应用于 次级。二级服务器分批收集oplog条目,然后并行应用这些批次。将按照出现在操作日志中的顺序应用写入。
从MongoDB 4.0开始,如果辅助数据库正在复制,则读取从数据的WiredTiger快照读取的 目标辅助数据库。这允许读取与复制同时进行,同时仍保证数据的一致视图。在MongoDB 4.0之前的版本中,将在所有正在进行的复制完成之前阻止对辅助数据库的读取操作。有关更多信息,请参见多线程复制。
因为单个文档可以包含相关数据,否则它们将在关系模式中的各个父子表之间建模,因此MongoDB的原子单文档操作已经提供了满足大多数应用程序数据完整性需求的事务语义。一个或多个字段可以通过一次操作编写,包括对多个子文档和数组元素的更新。MongoDB提供的保证可确保在文档更新时完全隔离。任何错误都会导致操作回滚,以使客户端获得一致的文档视图。
但是,对于需要对多个文档(在单个或多个集合中)进行读写原子性的情况,MongoDB支持多文档事务:
在版本4.0中,MongoDB支持副本集上的多文档事务。
在4.2版中,MongoDB引入了分布式事务,它增加了对分片群集上多文档事务的支持,并合并了对副本集上多文档事务的现有支持。
有关MongoDB中事务的详细信息,请参阅 事务页面。
重要
在大多数情况下,与单文档写入相比,多文档事务产生的性能成本更高,并且多文档事务的可用性不应代替有效的架构设计。在许多情况下, 非规范化数据模型(嵌入式文档和数组)对于您的数据和用例将继续是最佳的。也就是说,在许多情况下,对数据进行适当的建模将最大程度地减少对多文档交易的需求。
有关其他事务使用方面的注意事项(例如运行时限制和oplog大小限制),另请参见 生产注意事项。