Zhu.Yang

朱阳的个人博客(公众号:think123)

0%

MongoDB的锁事

MongoDB有4个粒度级别的锁

  1. Global(MongoD 实例) – 所有的数据库上加锁
  2. Database – 锁定某个数据库
  3. Collection – 锁定某个集合
  4. Document – 锁定某个文档

MongoDB本身只提供Global,Database,Collection三个级别的锁,Document级别的锁是由存储引擎提供的(Wired Tiger提供了Document级别的锁)

MongoDB允许多个客户端读取和写入相同的数据。为了确保一致性,它使用锁和其他并发控制措施来防止多个客户端同时修改同一条数据。总之,这些机制保证对单个文档的所有写入完全或根本不发生,并且客户端永远不会看到数据的不一致视图。如果你想保证多个文档的并发性,就需要使用事务,MongoDB从4.0版本开始支持多文档事务(包括复制集),但是对分片集群的事务支持要从4.2版本才开始支持。

MongoDB使用多粒度锁,允许操作锁定全局,数据库或集合级别,并允许各个存储引擎在集合级别下实现自己的并发控制(例如WiredTiger提供了文档级别的锁)。MongoDB使用读写锁,并且允许并发读共享对资源(database或者collection)的访问。

除了用于读取的共享(S)锁(读锁)和用于写入操作的排它(X)锁(写锁)之外,意向共享(IS)和意向排它(IX)锁表示使用更精细的粒度锁定读取或写入资源的意图。但我们锁定某个资源的时候,使用意向锁来锁定比它更高级别的资源,这也是多粒度锁的核心。

对一个数据行或者document加意向锁的含义是:当前事务想要在这个节点或者这个节点的子节点加某种锁(共享或者排它),有了意向锁以后,对任何数据加共享锁或者排它锁都要先加意向锁再加具体锁。

节点

当我们对d1这个document进行写入的时候,需要给它加上X锁(排它锁),这个时候要先去它上层的资源Global,D1(database),C1(collection)加上IX(意向排它锁),加了之后才能在d1上加实际的排它锁。如果实际的锁加在C1等非叶子节点情况也是一样的。

加意向锁最主要的作用就是在加实际的锁的时候,通过检查父节点是否已经存在了意向锁来确定当前是否可以加上锁。

比如d1上持有了实际的排它锁,这个时候C1,D1,Global都有一个意向排它锁。此时如果有另外的请求想要在C1上加实际的共享锁(读),但是C1已经有了意向排它锁了,所以就加不成了,需要等待。

如果此时想要在d2上加共享锁。此时需要先去它的上层节点添加意向共享锁,虽然C1,D1已经存在了意向排它锁,但是仍然可以加上意向共享锁,因为它们是兼容的。

把意向锁的相容性和实际锁放进一个表格进行对别下:

IS(意向共享) IX(意向排它) S(共享) X(排它)
IS(意向共享) true true true false
IX(意向排它) true true false false
S(共享) true false true false
X(排它) false false false false

对于大多数读写操作,WiredTiger存储引擎使用乐观并发控制。WiredTiger仅在全局,数据库和集合级别使用意向锁。当存储引擎检测到两个操作之间的冲突时,会发生写入冲突,导致MongoDB透明地重试该操作。一些全局操作(通常是涉及多个数据库的短期操作)仍然需要全局“实例范围”锁定。其他一些操作(例如删除集合)仍需要独占数据库锁。

我们MongoDB中使用db.serverStatus()或者db.currentOp()命令的输出中,锁模式表示如下:

锁模式 描述
R 共享锁(S)
W 排它锁(X)
r 意向共享锁(IS)
w 意向排它锁(IX)

所以我们要注意对应关系,不要弄错了。下面的表格列出了一些操作以及它们用于文档级锁定存储引擎的锁类型:

操作 Database Collection
query r(意向共享锁) r(意向共享锁)
insert w(意向排它锁) w(意向排它锁)
remove w(意向排它锁) w(意向排它锁)
update w(意向排它锁) w(意向排它锁)
aggregation(聚合) r(意向共享锁) r(意向共享锁)
create an index(前台) W(排它锁)
create an index(后台) w(意向排它锁) w(意向排它锁)
list collections r(意向共享锁)
map-reduce W(排它锁) 以及 R(共享锁) w(意向排它锁)以及r(意向共享锁)

欢迎关注我的其它发布渠道