【问题标题】:To what level does MongoDB lock on writes? (or: what does it mean by "per connection"MongoDB 将写入锁定到什么级别? (或:“每个连接”是什么意思
【发布时间】:2013-07-01 15:43:50
【问题描述】:

在 mongodb 文档中,它说:

从 2.2 版开始,MongoDB 为大多数读写操作在每个数据库的基础上实现锁。一些全局操作,通常是涉及多个数据库的短期操作,仍然需要全局“实例”范围的锁。在 2.2 之前,每个 mongod 实例只有一个“全局”锁。

这是否意味着在我有 3 个从网络上运行的不同应用程序到 mongodb://localhost/test 的连接的情况下 - 一次只能写入一个?还是只是每个连接?

IOW:是每个连接,还是整个 /test 数据库在写入时被锁定?

【问题讨论】:

  • 从 MongoDB 3.0 开始,WiredTiger 存储引擎在 64 位版本中可用。 WiredTiger 使用文档级并发控制进行写入操作。因此,多个客户端可以同时修改一个集合的不同文档。 docs.mongodb.com/manual/core/wiredtiger/…
  • MongoDB v4.4 Manual's FAQ: Concurrency中有关于锁和并发相关话题的简要解答:MongoDB使用什么类型的锁?锁的粒度如何?如何查看 mongod 实例上的锁状态?读或写操作是否会产生锁?一些常见的客户端操作使用了哪些锁?哪些管理命令会锁定数据库或集合? MongoDB 操作是否会锁定多个数据库?分片和副本集如何影响并发? MongoDB 支持事务吗?

标签: mongodb concurrency locking


【解决方案1】:

我知道这个问题已经很老了,但仍然有些人感到困惑......

从 MongoDB 3.0 开始,WiredTiger 存储引擎(使用文档级并发)在 64 位版本中可用。

WiredTiger 使用文档级并发控制进行写入操作。因此,多个客户端可以同时修改一个集合的不同文档。

对于大多数读写操作,WiredTiger 使用乐观并发控制。 WiredTiger 仅在全局、数据库和集合级别使用意图锁。当存储引擎检测到两个操作之间的冲突时,会引发写入冲突,导致 MongoDB 透明地重试该操作。

一些全局操作,通常是涉及多个数据库的短期操作,仍然需要全局“实例范围”锁。其他一些操作,例如删除集合,仍然需要独占数据库锁。

Document Level Concurrency

【讨论】:

  • 也许人们会感到困惑,因为 mongo 文档并不清楚“乐观并发控制”是什么意思。反刍文档并没有真正的帮助。
【解决方案2】:

Mongo 3.0 现在支持集合级锁定。

除此之外,现在 Mongo 创建了一个允许创建存储引擎的 API。 Mongo 3.0 自带 2 个存储引擎:

  1. MMAPv1:默认存储引擎,在之前的版本中使用。带有集合级锁定。
  2. WiredTiger:新的存储引擎,带有文档级锁定和压缩。 (仅适用于 64 位版本)

MongoDB 3.0 release notes

WiredTiger

【讨论】:

    【解决方案3】:

    MongoDB 锁定不同

    在 MongoDB 中的锁定不像在 RDBMS 中那样工作,因此需要进行一些解释。在早期版本的 MongoDB 中,只有一个全局读取器/写入器锁存器。从 MongoDB 2.2 开始,每个数据库都有一个读/写锁存器。

    读写器锁存器

    latch 是多读、单写的,并且是写贪婪的。这意味着:

    • 一个数据库中可以有无限数量的同时阅读者
    • 在任何一个数据库中的任何集合上一次只能有一个作者(稍后会详细介绍)
    • 作家屏蔽了读者
    • “写者贪婪”是指一旦有写请求进来,所有读者都会被阻塞,直到写完成(稍后会详细介绍)

    请注意,我将其称为“闩锁”而不是“锁”。这是因为它是轻量级的,并且在适当设计的模式中,写锁保持在十几个微秒左右。有关读写器锁定的更多信息,请参阅here

    在 MongoDB 中,您可以同时运行任意数量的查询:只要相关数据在 RAM 中,它们都会得到满足而不会发生锁定冲突。

    原子文档更新

    回想一下,在 MongoDB 中,事务级别是单个文档。对单个文档的所有更新都是原子的。 MongoDB 通过仅在更新 RAM 中的单个文档所需的时间内保持写锁存器来实现这一点。如果有任何运行缓慢的操作(特别是,如果需要从磁盘调入文档或索引条目),则该操作将产生写锁存器。当操作产生锁存器时,下一个排队的操作可以继续。

    这确实意味着对单个数据库中所有文档的写入都会被序列化。如果您的架构设计不佳,并且您的写入需要很长时间,这可能会成为问题,但在设计正确的架构中,锁定不是问题。

    作家-贪婪

    关于作家贪婪的更多话:

    一次只有一个写入器可以持有闩锁;多个读卡器一次可以握住闩锁。在一个简单的实现中,如果只有一个读取器在运行,写入器可能会无限期地挨饿。为了避免这种情况,在 MongoDB 实现中,一旦任何单个线程对特定的锁存器发出写请求

    • 需要该闩锁的所有后续读取器都会阻塞
    • 该作者将等到所有当前阅读者完成
    • 写入器将获取写入锁存器,完成其工作,然后释放写入锁存器
    • 所有排队的阅读器现在都将继续进行

    实际的行为是复杂的,因为这种对写者的贪婪行为以不明显的方式与让步相互作用。回想一下,从 2.2 版开始,每个数据库都有一个 单独的 锁存器,因此写入数据库“A”中的任何集合将获得一个单独的锁存器,而不是写入数据库“B”中的任何集合。

    具体问题

    关于具体问题:

    • MongoDB 内核只持有更新单个文档所需的锁(实际上是闩锁)
    • 如果您有多个连接进入 MongoDB,并且每个连接都在执行一系列写入,则锁存器将在每个数据库的基础上保留,只要该写入完成所需的时间李>
    • 执行写入(更新/插入/删除)的多个连接将全部交错

    虽然这听起来像是一个很大的性能问题,但实际上它并不会减慢速度。有了正确设计的架构和典型的工作负载,MongoDB 将在任何数据库的锁定百分比超过 50% 之前使磁盘 I/O 容量饱和——即使是 SSD 也是如此。

    据我所知,最大容量的 MongoDB 集群目前每秒执行 200 万次写入。

    【讨论】:

    • 我理解了作家贪婪“锁定”背后的逻辑——只是在这一点上它不会将其他人拒之门外。这有帮助。谢谢!
    • 我需要一些关于“写者贪婪”概念的精确性:当你说“一旦有写请求进来,所有读者都会被阻塞,直到写完成(稍后会详细介绍)”写请求阻止整个数据库或只是集合(或文档)上的所有读者?读操作会阻止写操作吗?谢谢
    • @FredMériot 目前它将在数据库级别阻止它,但文档级别锁定已经在 dev 分支中。是的,读取器操作可以阻止写入,MongoDB 无法一致读取是正在写入的东西
    • 即使死亡也不能阻止这家伙帮助人们!安息吧威廉
    • 对于那些想知道威廉发生了什么的人,请在此处阅读:blog.mongodb.org/post/99566492653/…。 RIP
    【解决方案4】:

    不是每个连接,而是每个mongod。换句话说,锁将存在于该服务器上与test 数据库的所有连接中。

    它也是一个读/写锁,所以如果正在发生写入,那么读取必须等待,否则 MongoDB 怎么知道它是一致读取?

    但是我应该提到,MongoDB 锁与您获得的 SQL/普通事务锁非常不同,通常锁会在平均更新之间保持大约一微秒。

    【讨论】:

    • 第二条语句有很大帮助 - 我找不到他们排队的任何地方,所以我担心它只是抓取可能不一致的数据。我也知道轻微的延迟,在我的特殊情况下很好。谢谢!
    • normally a lock will be held for about a microsecond 如果你持有一微秒的锁,那么根据物理定律你不能保证写入持久性
    • 截至 2015 年,没有具有 1µs 延迟的耐用设备,如果您在小于该值未持久化的时间内释放锁定。
    • 我不知道“fsync队列”是什么,也许是mongodb(在内存中)内部结构?无论如何,回到我最初的想法:如果您的写入操作需要 1µs(甚至像 William 所说的那样 0.5µs)才能完成,那么您不能保证数据到达耐用设备。
    • @PabloFernandez meh,人们会犯错误
    猜你喜欢
    • 2012-02-21
    • 2012-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-01
    • 2014-09-10
    • 2021-05-19
    • 1970-01-01
    相关资源
    最近更新 更多