MongoDB中的读写锁

1. MongoDB 使用的锁

MongoDB 使用的是“readers-writer”锁, 可以支持并发但有很大的局限性
当一个读锁存在,许多读操作可以使用这把锁,然而, 当一个写锁的存在,一个单一的写操作会”exclusively“持有该锁,同一时间其它写操作不能使用共享这个锁;
举个例子,假设一个集合里有10个文档,多个update操作不能并发在这个集合上,即使是更新不同的文档。


2. 锁的粒度

在 2.2 版本以前,一个mongodb实例一个写锁,多个读锁。也就是说mongod 只有全局锁(锁定一个server);

在2.2-3.0的版本,一个数据库一个写锁,多个读锁。例如如果一个 mongod 实例上有 5 个库,如果只对一个库中的一个集合执行写操作,那么在写操作过程中,这个库被锁;而其它 4 个库不影响。相比 RDBMS 来说,这个粒度已经算很大了!

在3.0之后的版本,WiredTiger提供了文档(不是集合)级别的锁。

更新:MongoDB 3.4版本,写操作的锁定粒度在表中数据记录(document)级别,即使操作对象可能是多条数据,每条数据在被写入时都会被锁定,防止其他进程写入;但是写操作是非事务性的,即写入多条数据,即使当前写入操作还没有完成,前面已经写入的数据也可以被其他进程修改。除非指定了$isolated,一次写入操作影响的数据无法在本次操作结束之前被其他进程修改。
$isolated也是非事务性的,即如果写入过程出错,已经完成的写入操作不会被rollback;另外,$isolated需要额外的锁,无法用于sharded方式部署的集群。
官网文档链接

MongoDB高吞吐的原因:

  • MongoDB 没有完整事务支持,操作原子性只到单个 document 级别,所以通常操作粒度比较小;
  • MongoDB 锁实际占用时间是内存数据计算和变更时间,通常很快;
  • MongoDB 锁有一种临时放弃机制,当出现需要等待慢速 IO 读写数据时,可以先临时放弃,等 IO 完成之后再重新获取锁。

 

3. 如何查看锁的状态

db.serverStatus()
db.currentOp()
mongotop # 类似top命令,每秒刷新
mongostat
the MongoDB Monitoring Service (MMS)


4. 哪些操作会对数据库产生锁?

下表列出了常见数据库操作产生的锁。

操作 锁定类型
查询 读锁
通过cursor读取数据 读锁
插入数据 写锁
删除数据 写锁
修改数据 写锁
Map-reduce 读写锁均有,除非指定为non-atomic,部分mapreduce任务可以同时执行(猜测是生成的中间表不冲突的情况下)
添加index 通过前台API添加index,锁定数据库一段时间
db.eval() 写锁,同时阻塞其他运行在MongoDB上的JavaScript进程
eval 写锁,如果设定锁定选项是nolock,则不会有些锁,而且eval无法向数据库写入数据
aggregate() 读锁

 

附上原文:
Operation Lock Type
Issue a query Read lock
Get more data from a cursor Read lock
Insert data Write lock
Remove data Write lock
Update data Write lock
Map-reduce Read lock and write lock, unless operations are specified as non-atomic. Portions of map-reduce jobs can run concurrently.
Create an index Building an index in the foreground, which is the default, locks the database for extended periods of time.
db.eval() Write lock. db.eval() blocks all other JavaScript processes.
eval Write lock. If used with the nolock lock option, the eval option does not take a write lock and cannot write data to the database.
aggregate() Read lock

 

5. 哪些数据库管理操作会锁数据库?

某些数据库管理操作会 exclusively 锁住数据库,以下命令需要申请 exclusively 锁,并锁定一段时间

db.collection.ensureIndex(),
reIndex,
compact,
db.repairDatabase(),
db.createCollection(), when creating a very large (i.e. many gigabytes) capped collection,
db.collection.validate(),
db.copyDatabase() # 可能会锁定所有数据库(database)


以下命令需要申请 exclusively 锁,但锁定很短时间。

db.collection.dropIndex(),
db.collection.getLastError(),
db.isMaster(),
rs.status() (i.e. replSetGetStatus,)
db.serverStatus(),
db.auth(), and
db.addUser().

备注:可见,一些查看命令也会锁库,在比较繁忙的生产库中,也会有影响的。


6. MongoDB内部可能锁住所有库的操作

以下数据库内部操作会锁定多个库。

日志管理 MongoDB的内部操作,每个一段时间就锁定所有数据库,所有的数据库共享一份日志
用户认证 锁定admin数据库和用户正在申请访问的数据库
所有写入备份数据库(replica)的操作都会锁定写入目标数据库和本地数据库,本地数据库的写入锁允许写入主节点的oplog

原文:
Journeying, which is an internal operation, locks all databases for short intervals. All databases share a single journal.
User authentication locks the admin database as well as the database the user is accessing.
All writes to a replica set’s primary lock both the database receiving the writes and the local database. The lock for the local database allows the mongod to write to the primary’s oplog.

posted on 2019-04-19 17:53  duanxz  阅读(9700)  评论(0编辑  收藏  举报