MongoDB 基础系列十八:索引之 Multikey Index Bounds

前言

此篇博文是 Mongdb 基础系列之一;

这章的内容直接读取官文英文原文比较难懂,所以,单独创建一个章节来整理学习;

本文为作者的原创作品,转载需注明出处;

简介

The bounds of an index scan define the portions of an index to search during a query. When multiple predicates over an index exist, MongoDB will attempt to combine the bounds for these predicates, by either intersection or compounding, in order to produce a scan with smaller bounds.

备注,这里的 bounds 要理解为“边界”的意思;predicates 要理解为“查询条件”的意思;

一系列用来扫描索引的“边界( bounds )”,在一个查询中限定了对一个索引进行扫描(或查询)的部分;

停,这句话怎么理解?index 是一个整体,在查询的时候,通过 bounds 定义了该 index 在查询的过程中被检索( 被查询 )的部分;

继续,如果有针对同一个索引的多个“查询条件( predicates )”存在,

停,这句话又该怎么理解?这里的“查询条件( predicates )”对应的其实就是前文所描述的“边界( bounds )”,因为,“查询条件 predicates”定义了“边界 bounds”;

MongoDB 为了对 index 生成一个具备更小规模 bounds 的扫描,将会通过“交叉 intersection”或者“复合 compouding”的方式试图去合并这些“边界”;(备注,正如前文笔者所描述的那样,这里的 bounds 指的就是“查询条件 predicates”);因此,最终也就可以得到更少的“查询条件”,其实这么做的意义也就是为了提升对 index 扫描的性能;

Intersect Bounds for Multikey Index (使得 Multikey Index 的边界交叉)

注意,通过简介的分析可知,bounds 的意思就是指“查询条件”的意思;

“边界”交叉的意思就是将多个“查询条件 (multiple bounds)”用 AND 操作符进行链接;比如,我们有这样的查询条件( 既是 bounds ),[ [ 3, Infinity ] ] 和 [ [ -Infinity, 6 ] ],通过交叉以后,我们得到的 bounds 是 [ [ 3, 6 ] ];

假设我们有如下的 survey 数据,

1
2
{ _id: 1, item: "ABC", ratings: [ 2, 9 ] }
{ _id: 2, item: "XYZ", ratings: [ 4, 3 ] }

在 ratings 字段上创建索引,

1
db.survey.createIndex( { ratings: 1 } )

然后执行如下的查询,

1
db.survey.find( { ratings : { $elemMatch: { $gte: 3, $lte: 6 } } } )

上述使用了 $elemMatch,意思是匹配 ratings 字段的时候必须同时满足这两个条件;在 MongoDB 执行查询的时候,会将两个条件分别解释为,

  • \$gte: 3 翻译为 [ [ 3, Infinity ] ]
  • \$lte: 6 翻译为 [ [ -Infinity, 6 ] ]

然后,将上述的两条查询通过 intersect 操作进行交叉,得到如下的 bound,

1
ratings: [ [ 3, 6 ] ]

进行 itersect 操作既交叉动作的前提是多个 bounds 之间有交叉的可行性,比如下面这个例子,就没有交叉的可行性,

1
db.survey.find( { ratings : { $gte: 3, $lte: 6 } } )

Compound Bounds for Multikey Index

Intersect Bounds 主要用于对单个字段的多个 Bounds 进行合并操作;而 Compound Bounds 则主要是针对多个不同字段(包含一个数组字段)的查询所进行的查询优化操作;

继续以上述的 survey 数据为例,

1
2
{ _id: 1, item: "ABC", ratings: [ 2, 9 ] }
{ _id: 2, item: "XYZ", ratings: [ 4, 3 ] }

然后,我们在 item 和 ratings 两个字段上同时创建符合索引( Compound Index ),

1
db.survey.createIndex( { item: 1, ratings: 1 } )

注意,这里的 ratings 上的索引是 Multikey Index;

不能交叉的 Compound Bounds

然后,我们执行如下的查询操作

1
db.survey.find( { item: "XYZ", ratings: { $gte: 3 } } )

然后,MongoDB 会分别针对上述两个条件执行如下的查询解析工作,

  • item: “XYZ” 条件转换为 [ [ “XYZ”, “XYZ” ] ]
  • ratings: { $gte: 3 } 条件转换为 [ [ 3, Infinity ] ]

最终所生成的用于在 index 上查询的条件是,

1
{ item: [ [ "XYZ", "XYZ" ] ], ratings: [ [ 3, Infinity ] ] }

为什么第一个条件要这么写呢?item: [ [ “XYZ”, “XYZ” ] ],这样写的目的是既是为了限制其查询的范围;

可被交叉的 Compound Bounds

假如,我们执行下面的查询操作,

1
2
3
db.survey.find( {
item: { $gte: "L", $lte: "Z"}, ratings : { $elemMatch: { $gte: 3, $lte: 6 } }
} )

MongoDB 在执行查询的时候,会将上述对 index 的查询优化如下,

1
"item" : [ [ "L", "Z" ] ], "ratings" : [ [3.0, 6.0] ]

可见,上述的 Compound Index 是可以通过交叉的方式进行优化的;而需要注意的是,交叉的操作永远是针对同一个字段的不同查询条件进行的;