唯一索引如何真正起作用并避免冲突?

How do unique indexes really work and avoid collisions?

假设我有一个集合,我在其中创建了一个字段的唯一索引:

db.users.createIndex({username: 1}, {unique:true})

如果将两个具有相同用户名的文档同时插入到集合中会怎样?
数据库是如何防止碰撞的?我的意思是哪个被插入,哪个导致错误?
假设插入确实是同步的,那么数据库无法知道插入了两个重复项,对吗?
那么,到底发生了什么?

写入不能同时应用于数据集。当写入发送到 MongoDB 实例时,无论是分片还是独立服务器,都会发生以下情况

  1. 请求一个集合范围的写锁(驻留在 RAM 中)
  2. 授予锁定后,将根据唯一索引(通常驻留在 RAM 中)检查要写入的结果数据(无论是更新、更新插入还是新文档)
  3. 如果没有碰撞,则将数据应用到RAM中的数据集
  4. 锁被释放。只有现在其他写入才能开始对内存中的数据执行更改。
  5. 使用默认写入关注,查询 returns 现在
  6. commitIntervalMs之后数据被写入日志
  7. 仅在 syncInterval 秒(默认为 60 秒)后,日志才会应用于数据文件

话虽如此,我们还是可以看看实际值。每秒 100 万次写入对于单个服务器来说似乎有点多(仅仅是因为海量存储无法处理它),因此我们假设一个具有 10 个分片的分片集群,以及一个或多或少均匀分布写入的分片键。正如我们在上面看到的,所有操作都在 RAM 中应用。使用今天的硬件,可以处理大约 35 亿 instructions/s,或每纳秒 3.5 条指令。假设获取和释放锁各需要 35 条指令或 10 纳秒。因此,我们每次 100k 写入的锁定和解锁将花费 20 纳秒,总共是 1/500 秒。

这将为 MongoDB 需要做的其他事情留下 499/500 秒或 998000000 纳秒,这转化为高达 3.493 十亿 指令。

防止并发写的锁远不是写操作的限制因素。将更改同步到日志和数据文件通常是限制因素,其次是减少 RAM 以将索引和工作集保留在 RAM 中。