- Frequently Asked Questions >
- FAQ: 并发
FAQ: 并发¶
在 2.2 版更改.
MongoDB 通过使用系统锁,允许多个客户端同时读写单独的数据库,并且确保所有的客户端得到同样的数据视图,防止多个应用同时修改同一块数据的确切位置,锁机制帮助确保单一文档的所有写操作完全发生或不发生。
MongoDB使用的是什么类型的锁?¶
MongoDB 使用一个readers-writer [1] 锁,它允许并发多个读操作访问数据库,但是只提供唯一写操作访问。
当一个读操作锁存在时,许多读操作可以使用这个锁。然而,当一个写操作存在时,一个写操作会排他性的保持这个锁,并且不能有其他的读操作或者写操作可以共享这个锁。
锁是”写贪婪,” 这意味着写操作锁比读操作具有优先权,当写操作和读操作同时等待一个锁,MongoDB会授予读操作这个锁。
[1] | 你可能很熟悉”readers-writer”锁, “multi-reader”锁或”shared exclusive” 锁。参见 Wikipedia 页 Readers-Writer 锁 获得更多 信息。 |
MongoDB中锁的颗粒度是什么?¶
在 2.2 版更改.
从2.2版本开始,MongoDB 在每个数据库基础上实现了大多数读操作和写操作的锁。一些全局操作,通常涉及多个数据库的短暂操作,依然需要一个全局实例大锁,在2.2版本之前,这是仅仅是一个全局锁在每个 mongod 实例上。
举个例子,如果你有六个数据库和一个数据库级别的写锁,其他的五个数据库的读写依然有效。一个全局锁在操作期间会使所有的六个数据库都不可用。
在我的 mongod 实例中如何看到锁的状态?¶
要获得锁使用信息的报告,使用下列方法:
特别的, locks 文档在 serverStatus </reference/command/serverStatus>`, 或者 locks 字段在 current operation reporting 提供了锁的类型和锁争夺的数量的观察在你的 :program:`mongod 实例.
终止一个操作,使用 db.killOp().
一个读操作或写操作产生锁吗?¶
在某些情况下,读操作和写操作产生自己的锁。
长期运行的读操作和写操作,比如查询,更新,删除,在很多情况下会产生锁。MongoDB 基于可预测磁盘访问模式使用一个自适应算法允许操作产生锁。(比如:页面错误。)
MongoDB 影响到大量文档的独立文档更新的写操作也能产生锁,比如:带有多个参数的 update() 。
MongoDB在执行读操作之前, 使用基于访问模式的探查方法来与之数据是否可能在物理内存中。如果MongoDB 预知 数据不在物理内存中,操作将产生自己的锁当直到MongoDB 加载数据到内存中。一旦数据在内存中是可用的,这个操作将再次获得这个锁来完成本次操作。
在 2.6 版更改: MongoDB 从扫描一个索引一直到预测出这个索引不在内存中的时候不会产生锁。
哪些操作会锁住数据库?¶
在 2.2 版更改.
下表列出了常用的数据库操作和他们使用锁的类型。
操作 |
锁类型 |
---|---|
查询操作 |
读锁 |
从一个 cursor 中获得更多数据 |
读锁 |
插入数据 |
写锁 |
删除数据 |
写锁 |
更新数据 |
写锁 |
Map-reduce | 读锁和写锁,除非被指定为非原子性操作。部分 map-reduce 的工作可以同时运行。 |
创建一个索引 |
在前台创建一个索引,这是默认的,会长时间的锁定数据库。 |
db.eval() | 写锁, db.eval() 方法在评估JavaScript函数的时候使用了一个全局写锁,你可以使用 eval 命令,带上 nolock: true 。 |
eval | 写锁。默认的, eval 命令在评估JavaScript函数的时候使用一个全局写锁。如果使用参数 nolock: true , eval 命令在评估JavaScript函数的时候不会使用全局写锁。然而,JavaScript函数可能为写操作接收一个写锁。 |
aggregate() | 读锁 |
哪些管理命令会锁定数据库?¶
某些管理命令会长时间排他性的锁定数据库。在一些部署中,对于大的数据库,你可能会考虑让 mongod 实例脱机,这样客户端不会受影响。比如:如果 mongod 是一个 复制集 的一部分,让:program:mongod 脱机让集群中的其他成员来负载,当维护进行中的时候。
下列的管理命令会在数据库上申请一个排他性的(比如:写操作)锁很长时间。
db.collection.ensureIndex() ,在不设置``background`` 为 ``true``时候。
- reIndex,
- compact,
- db.repairDatabase(),
db.createCollection() , 在创建一个非常大(比如:很多G)的capped collection ,(固定大小集合)
- db.collection.validate(), and
db.copyDatabase() 。这个操作可能锁住所有的数据库。参见 MongoDB 操作是否会锁多个数据库?.
下列的管理操作会锁住数据库,但是只保持锁非常短的时间:
MongoDB 操作是否会锁多个数据库?¶
下列MongoDB 操作会锁多个数据库:
db.copyDatabase() 必须一次锁住整个 mongod 实例。
db.repairDatabase() 获得一个全局写锁,将阻止其他的操作,直到完成。
Journaling ,这是一个内部操作,在很短的时间内锁住所有的数据库,所有的数据库共享一个日志。
User authentication 请求一个读锁在 admin 数据库中, 部署使用 2.6 user credentials。 使用 2.4 模式来部署用户凭证,验证锁住 admin 数据库,以及用户访问数据库。
所有对复制集的写操作 primary 会锁住数据库接收写操作和 local 数据库很短的时间。local 数据库的锁允许 mongod 写入主节点的 oplog ,这占用整个操作总时间的很小的部分。
分片如何影响并发?¶
Sharding 通过分布数据集在多个 mongod 实例上来改善并发,允许分片服务器 (比如:program:mongos 进程) 来执行任意数量的操作并发到不同的下游 mongod 实例。
每个 mongod 实例不依赖其他的成员在分片集群中,使用 MongoDB readers-writer lock 。这些操作在一个 mongod 实例上不会阻止 在其他成员上的操作。
并发如何影响复制集中的主节点?¶
In replication, 在 MongoDB 写操作到 primary 一个数据集时, MongoDB 同时写入主节点的 oplog, 这是一个特别的集合在 local 数据库中。 因此, MongoDB 必须同时锁住这个数据集所在的数据库和 local 数据库。 mongod 必须在同一时间同时锁住两个数据库以保持数据库的一致性和确保这个写操作,与复制一样,是 “all-or-nothing” (全或无) 的操作。