并发性常见问题解答
本页内容
MongoDB 允许多个客户端同时读写相同的数据。为了确保一致性,MongoDB 使用锁和并发控制 防止客户端同时修改相同的数据。对单个文档的写入要么全部发生,要么完全不发生,并且客户端始终看到一致的数据。
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 | 表示意向独占(IX)锁定。 |
[1] | 请参阅维基百科上的多粒度锁获取更多信息。 |
MongoDB中的锁粒度是多少?
对于大多数读写操作,WiredTiger使用乐观并发控制。WiredTiger仅在全局、数据库和集合级别使用意向锁。当存储引擎检测到两个操作之间的冲突时,其中一个将产生写冲突,导致MongoDB透明地重试该操作。
一些涉及多个数据库的短暂全局操作仍需要全局“实例级”锁。其他一些操作,例如renameCollection
,在特定情况下仍需要独占数据库锁。
如何查看我的mongod
实例的锁状态?
要报告锁的利用率信息,请使用以下任何方法
具体来说,locks
文档在serverStatus输出中,或locks
字段在当前操作报告
中,可以提供您mongod
实例中锁的类型和锁争用量的洞察。
在db.serverStatus()
和db.currentOp()
输出中,锁定模式表示如下
锁定模式 | 描述 |
---|---|
R | 表示共享(S)锁定。 |
W | 表示独占(X)锁定。 |
r | 表示意向共享(IS)锁定。 |
w | 表示意向独占(IX)锁定。 |
要终止操作,请使用db.killOp()
。
读取或写入操作是否会产生锁?
在某些情况下,读取和写入操作可以释放它们的锁。
长时间运行的读取和写入操作,如查询、更新和删除,在许多条件下会释放锁。MongoDB操作在影响多个文档的写入操作中,也可以在单个文档修改之间释放锁。
对于支持文档级并发控制的存储引擎,如WiredTiger,在访问存储时无需释放,因为全局、数据库和集合级别的意向锁不会阻止其他读取器或写入器。然而,操作将定期释放,例如
避免长时间运行的存储事务,因为这些可能需要保留大量数据在内存中;
作为中断点,以便您可以终止长时间运行的操作;
允许需要独占访问集合的操作,例如索引/集合的删除和创建。
常见的客户端操作会占用哪些锁?
以下表格列出了某些操作以及它们用于文档级别锁定存储引擎所使用的锁类型。
操作 | 数据库 | 集合 |
---|---|---|
执行查询 | r (共享意图) | r (共享意图) |
插入数据 | w (独占意图) | w (独占意图) |
删除数据 | w (独占意图) | w (独占意图) |
更新数据 | w (独占意图) | w (独占意图) |
执行聚合 | r (共享意图) | r (共享意图) |
创建索引 | W (独占) | |
列出集合 | r (共享意图) | |
Map-reduce | W (独占)和 R (共享) | w (独占意图)和 r (共享意图) |
注意
创建索引需要在集合上获取独占(W)锁。然而,锁不会在整个索引构建过程中保持。
有关更多信息,请参阅已填充集合上的索引构建。
哪些管理命令会锁定数据库?
一些管理命令可以长时间独占锁定数据库。对于大型集群,请考虑将 mongod
实例关闭,以防止客户端受到影响。例如,如果 mongod
是一个 副本集 的一部分,请将 mongod
关闭,并让副本集的其他成员在维护期间处理请求。
执行扩展锁的行政命令
这些行政操作需要在数据库级别上持续较长时间进行独占锁
此外,renameCollection
命令和相应的db.collection.renameCollection()
命令会采取以下锁
命令 | 锁行为 |
---|---|
renameCollection 数据库命令 | 如果在同一数据库中重命名集合,则 如果目标命名空间与源集合在不同的数据库中,则在跨数据库重命名集合时, |
renameCollection() 命令行辅助方法 | renameCollection() 方法对源和目标集合进行独占(W)锁,并且不能在数据库之间移动集合。 |
哪些管理命令会锁定一个集合?
以下管理操作需要在集合级别上获取独占锁
create
命令和相应的db.createCollection()
和db.createView()
壳方法。createIndexes
命令和相应的db.collection.createIndex()
和db.collection.createIndexes()
壳方法。构建过程只在索引构建的开始和结束时对集合持有独占锁。drop
命令和相应的db.collection.drop()
壳方法。dropIndexes
命令和相应的db.collection.dropIndex()
和db.collection.dropIndexes()
壳方法。renameCollection
命令和相应的db.collection.renameCollection()
壳方法会根据版本获取以下锁对于
renameCollection
和db.collection.renameCollection()
:如果在同一数据库内重命名集合,则该操作会获取源集合和目标集合的独占(W)锁。对于
renameCollection
命令仅限:如果目标命名空间与源集合位于不同的数据库中,则在数据库间重命名集合时,操作会对目标数据库进行独占(W)锁定,并在此期间阻止该数据库上的其他操作。
reIndex
命令和相应的db.collection.reIndex()
shell方法将获取集合的独占(W)锁定,并在完成前阻止集合上的其他操作。replSetResizeOplog
命令将获取oplog
集合的独占(W)锁定,并在完成前阻止集合上的其他操作。
MongoDB操作是否会锁定多个数据库?
这些MongoDB操作可能会获取并保持对多个数据库的锁定。
操作 | 行为 |
---|---|
这些操作仅获取独占(W)集合锁定,而不是全局独占锁定。 | |
在数据库间重命名集合时,此操作会获取目标数据库的独占(W)锁定、源数据库的意向共享(r)锁定和源集合的共享(S)锁定。 在同一个数据库中重命名集合时,此操作只需要对源集合和目标集合进行独占(W)锁定。 | |
此操作仅获取 oplog 集合的独占(W)锁定,而不是全局独占锁定。 |
分片如何影响并发性?
分片通过在多个 mongod
实例间分布集合,提高了并发性,允许分片服务器(特别是 mongos
进程)与下游的 mongod
实例并发运行。
在分片集群中,锁应用于每个单独的分片,而不是整个集群;即每个 mongod
实例在分片集群中独立于其他实例,并使用自己的 锁。一个 mongod
实例上的操作不会阻塞其他实例上的操作。
并发性如何影响副本集的主节点?
在副本集中,当 MongoDB 向 主节点 上的集合写入时,MongoDB 也会向主节点的 操作日志(oplog)写入,这是一个位于 local
数据库的特殊集合。因此,MongoDB 必须锁定集合的数据库和 local
数据库。为了保持数据库的一致性并确保即使有复制,写操作也是全部或无操作,mongod
必须同时锁定这两个数据库。
并发如何影响从节点?
在复制中,MongoDB不会将写操作顺序应用到从节点。从节点批量收集操作日志条目,然后并行应用这些批次。写操作按其在操作日志中出现的顺序应用。
如果从节点正在进行复制,则针对目标从节点读取的数据是从WiredTiger数据快照中读取的。这允许读取与复制同时发生,同时仍然保证数据的一致性视图。
MongoDB支持事务吗?
由于单个文档可以包含在关系数据库模式中通常在单独的父子表中建模的相关数据,MongoDB的原子单文档操作已经提供了满足大多数应用程序数据完整性需求的事务语义。一个或多个字段可以在单个操作中写入,包括对多个子文档和数组元素的更新。MongoDB提供的保证确保在更新文档时完全隔离;任何错误都会导致操作回滚,从而使客户端接收到文档的一致视图。
对于需要将读和写操作原子化到多个文档(在单个或多个集合中)的情况,MongoDB支持分布式事务,包括副本集和分片集群上的事务。
更多信息,请参阅事务。
重要
在大多数情况下,分布式事务相较于单文档写入会带来更高的性能成本,因此分布式事务的可用性不应替代有效的架构设计。在许多场景中,非规范化数据模型(嵌入文档和数组)将继续是您数据和用例的最佳选择。也就是说,在许多场景中,适当地建模您的数据将最大限度地减少分布式事务的需求。
有关事务使用的其他考虑因素(例如运行时限制和oplog大小限制),请参阅生产注意事项。
MongoDB提供了哪些隔离保证?
根据读取关注点,客户端可以在写入完成之前看到写入的结果。为了控制读取的数据是否可能回滚,客户端可以使用readConcern
选项。
无锁读操作是什么?
新版本5.0.
无锁读操作立即运行:当其他操作在集合上拥有独占(X)写锁时,它不会被阻塞。
从MongoDB 5.0版本开始,以下读操作在另一个操作在集合上持有独占(X)写锁时不会被阻塞:
在向集合写入时,mapReduce
和 aggregate
持有排他性(IX)锁。因此,如果已在一个集合上持有排他性X锁,mapReduce
和 aggregate
写入操作将被阻塞。
有关信息,请参阅