Mongodb后台创建索引的问题

简介: Mongodb创建索引分为前台和后台,前台创建索引将阻塞所有对该Collection的读写操作,后台创建索引不阻塞对该Collection的读写操作,那么问题来了,后台创建索引的同时,Collection正在被读写,如何保证索引创建的准确性呢?

后台创建索引,遍历Collection表的所有数据之前,会先把数据库的锁从MODE_X变成MODE_IX, (关于数据库多级锁的概念,https://en.wikipedia.org/wiki/Multiple_granularity_locking), 从而允许其他客户端对数据库读写操作.一面做全表扫面,一面做数据的更新,如果保证索引和数据能对的上呢?

事实上Mongodb是不提供事务保证的,只对单个文档的操作提供了原子操作保证,所以如果执行一个update多个值的操作,不能保证要更新的多个值的原子性.假如现在更新操作要更改两个值(key1,value1),(key2,value2),你在更新(key1,value1)时候,有可能另一个人更改了(key2,value2),等你再去更新(key2,value2)的时候,由于你获取的是原来的版本,所以更新时候会产生WCE(写冲突异常),你需要重新获取(key2,value2)的新版本,在新版本基础上再执行更新操作.事实上,Mongodb在对每个值做操作之前都会重新获取一次这个值的最新版本.就WiredTiger存储引擎而言,每次获取一个新版本,相当于创建了一个新的快照,在这个快照基础上做一次事务.快照隔离级别对应ANSI SQL定义的可重复读隔离级别.了解了这些我们再说后台创建索引的问题.
我们将创建索引和对数据的读写操作的竞争分为几种场景来描述:

a 对key已经创建了索引,对key做修改操作
b 对key还没创建索引,对key做修改操作
c 对key做创建索引的同时,对key做修改操作

从上面的三个场景来看,a,b是不会有竞争条件出现的,两个操作是一前一后执行.

a 的情况key已经做了索引的创建,对key的修改操作会先删除key的旧值索引,再对key的新值做索引.
b 的情况key还没有创建索引,对key的修改操作先删除老的索引,因为还没有索引,所以删除返回WT_NOTFOUND,不做处理.再对key的新值做索引.等后台创建索引操作为这个key创建索引的时候拿的是key的新版本创建索引,因为索引已经创建所以会产生写入冲突,跳过为这key创建索引,继续为下一个key创建索引

对于问题c是我们要讨论的竞争条件,这个问题曾经引发Mongodb索引创建的bug,目前已经修改,我们来描述这个bug的产生条件

During an index build, the collection is locked in mode IX. Consider a background index build occurring with concurrent updates. Then, the following race scenario could occur:

  1. Both the updater and the index builder read the same document.
  2. To perform the update, the updater unindexes the document's old value.
  3. Because the document has yet to be indexed, the WiredTiger returns WT_NOTFOUND. No WCE is generated.
  4. The update proceeds to index the new value.
  5. The indexer in the background, unaware of any WCEs, indexes the value it has read. The result is that the index has one too many keys.
    We can fix this by having the update path trigger a write conflict exception even in the "no-op" case when unindexing returns WT_NOTFOUND.

(An alternative is to simply lock the collection in X mode during an index build, but this will probably have a large impact on performance.)

以上描述的场景,最终产生的结果是,更新操作为document的新版本创建一个索引,后台创建索引操作会为document老版本创建一个索引.显然这是错误的,错误产生的原因是更新操作为新值创建了索引,后台创建索引操作为老值创建了索引,两个操作读取的数据集合相同,但是更新的数据集合没有交集,所以两个快照同时操作不会产生写冲突.更新操作修改的数据集合是(NewValue),后台创建索引修改的数据集合是(OldValue).
但是明明是操作的同一个key,这个冲突应该产生才对,为了解决这个问题,Mongodb的开发者在步骤2)上删除老索引返回WT_NOTFOUND的时候,故意插入老版本数据的索引,再删除它,这样更新操作修改的数据集合是(OldValue,NewValue),后台创建索引修改的数据集合是(OldValue),因为对修改了OldValue就使更新操作和后台创建索引操作产生写冲突,后台创建索引操作会跳过为这key创建索引,继续为下一个key创建索引

这个bug,详见
https://jira.mongodb.org/browse/SERVER-23807

目录
相关文章
|
9月前
|
存储 NoSQL MongoDB
微服务——MongoDB常用命令——MongoDB索引知识概述
本文介绍MongoDB索引相关知识,包括其在查询中的重要作用。索引可避免全集合扫描,显著提升查询效率,尤其在处理海量数据时。通过B树数据结构存储字段值并排序,支持相等匹配、范围查询及排序操作。文中还提供了官方文档链接以供深入学习。
162 0
|
存储 NoSQL MongoDB
掌握MongoDB索引优化策略:提升查询效率的关键
在数据库性能调优中,索引是提升查询效率的利器。本文将带你深入了解MongoDB索引的内部工作原理,探讨索引对查询性能的影响,并通过实际案例指导如何针对不同的查询模式建立有效的索引。不仅将涵盖单一字段索引,还会探讨复合索引的使用,以及如何通过分析查询模式和执行计划来优化索引,最终实现查询性能的最大化。
|
9月前
|
存储 NoSQL MongoDB
微服务——MongoDB常用命令——MongoDB索引的类型
本节介绍了MongoDB中索引的几种类型及其特点。包括单字段索引,支持升序/降序排序,索引顺序对操作无影响;复合索引,字段顺序重要,可实现多级排序;地理空间索引,支持平面与球面几何查询;文本索引,用于字符串搜索并存储词根;哈希索引,基于字段值散列,适合等值匹配但不支持范围查询。
238 1
微服务——MongoDB常用命令——MongoDB索引的类型
|
监控 NoSQL MongoDB
MongoDB数据库的索引管理技巧
【8月更文挑战第20天】MongoDB数据库的索引管理技巧
278 1
|
9月前
|
存储 NoSQL 定位技术
MongoDB索引知识
MongoDB索引是提升查询性能的关键工具,通过构建特殊的数据结构(如B树)优化数据访问路径。无索引时,查询需全集合扫描,时间复杂度为O(n);使用索引后可降至O(log n),实现毫秒级响应。MongoDB支持多种索引类型:单字段索引适用于高频单字段查询;复合索引基于最左前缀原则优化多条件过滤和排序;专业索引包括地理空间索引(支持LBS服务)、文本索引(全文搜索)和哈希索引(分片键优化)。合理选择和优化索引类型,可显著提升数据库性能。建议使用explain()分析查询计划,并定期清理冗余索引。
|
NoSQL Java API
MongoDB 强制使用索引 hint
MongoDB 强制使用索引 hint
527 3
|
存储 监控 NoSQL
MongoDB索引解析:工作原理、类型选择及优化策略
MongoDB索引解析:工作原理、类型选择及优化策略
|
存储 NoSQL 关系型数据库
MongoDB索引知识
MongoDB索引知识
118 1
MongoDB索引知识
|
存储 NoSQL MongoDB
MongoDB 索引限制
10月更文挑战第22天
219 2
|
NoSQL MongoDB 索引
MongoDB 高级索引
10月更文挑战第22天
107 2

推荐镜像

更多