0%

B+树的复合索引

1. 规则

1.1 最左匹配原则

MongoDB 中复合索引的使用和 MySQL 中复合索引的使用类似,也有最左匹配原则。即最左优先,在检索数据时从复合索引的最左边开始匹配。

复合索引创建的时候有一个一个基本的原则就是将选择性最强的列放到最前面。

选择性最高值得是数据的重复值最少,因为区分度高的列能够很容易过滤掉很多的数据。组合索引中第一次能够过滤掉很多的数据,后面的索引查询的数据范围就小了很多了。

1.2 ESR 原则

MongoDB 中复合索引的使用,对于索引的创建的顺序有一个原则就是 ESR 规则。

1

结果是 {gender:1, join_date:1, age: 1}

因为碰到范围查询,B+ 树后面的查询就不能使用索引了,排序就会在内存中进行。这就有了 ESR 规则,将范围查询的索引建在复合索引的最后面。

从左到右依次为:全等字段(E, Equality) -> 排序字段(S, Sort) -> 范围字段(R, Range)

  1. 如果有 【多个】 排序字段时,排序字段的顺序要与查询条件中排序字段的顺序一致,并且升降序也要一致。

  2. 如果有 【多个】 范围字段时,将区分度越 【低】 的字段放在更前面。

    例如:性别 -> 年龄 -> 籍贯
    性别的区分度为2,年龄的区分度为100,籍贯的区分度为10000

举一个栗子🌰

有一个复合索引为 { a:1, b:1, c:1, d:1 }

查询条件索引命中情况
db.data.find( { a: 5 } ).sort( { b: 1, c: 1 } ){ a: 1 , b: 1, c: 1 }
db.data.find( { b: 3, a: 4 } ).sort( { c: 1 } ){ a: 1, b: 1, c: 1 }
db.data.find( { a: 5, b: { $lt: 3} } ).sort( { b: 1 } ){ a: 1, b: 1 }

2. 其他

2.1 MongoDB 使用 B 树还是 B+ 树?

MongoDB 官网中有一段描述写的是MongoDB索引使用 B-tree 数据结构。

Indexes are special data structures that store a small portion of the collection’s data set in an easy-to-traverse form. MongoDB indexes use a B-tree data structure.

The index stores the value of a specific field or set of fields, ordered by the value of the field. The ordering of the index entries supports efficient equality matches and range-based query operations. In addition, MongoDB can return sorted results using the ordering in the index.

大致意思就是 MongoDB 使用的是 B-tree 数据结构,支持等值匹配和范围查询。可以使用索引的排序返回排序的结果。

而国内很多人喜欢把 B-tree 译作 B-树,这是个非常不好的直译,很容易让人产生误解,人们可能会以为 B-树 和 B树 是两种树。MongoDB 官网中的介绍中明确的表示 MongoDB 支持范围查询,所以我们可以得出结论用的就是 B+ 树。官网中讲的 B 树,指广义上的 B 树,因为 B+ 树也是 B 树的变种也能称为 B 树。

所以可以得出结论 MongoDB 默认的存储引擎 WiredTiger 目前使用的是 B+ 树索引结构。

2.2 区分度低的字段可以索引吗

An index can help even on low cardinality fields if,When the index covers all fields used in the query。

1
2
3
CREATE INDEX (low_cardinality_record, value)

SELECT SUM(value) FROM mytable WHERE low_cardinality_record = 3

结论:可以。

2.3 排序影响

在 MongoDB 中,排序的字段我们可以添加索引来保证排序的高效性,如果排序的字段没有添加索引或者添加的索引没有命中,那么排序就会在内存中进行。

因为排序效率不高,在查询条件带有排序的操作情况,一般考虑将排序字段也添加到组合索引中。至于字段的先后顺序,可以参见上文中的 ESR 规则。

单字段创建索引,无论是升序还是降序,对 sort 查询没有影响,MongoDB 可以从任意一方向遍历索引。但是复合索引,排序字段的升序降序对查询的的结果就有影响了。

假定有一个集合 events ,其中包含两个字段 username 和 date

1
2
3
4
5
6
7
8
9
10
-- 创建索引 
db.events.createIndex( { "username" : 1, "date" : -1 } )

-- 下面的两种查询能够命中索引:
db.events.find().sort( { username: 1, date: -1 } )
db.events.find().sort( { username: -1, date: 1 } )


-- 下面的这种查询就不能命中了
db.events.find().sort( { username: 1, date: 1 } )

B+ 数上的结构,username 从左到右是相对升序的。date 字段相对 username 字段,从左到右是降序的。
如果执行 db.events.find().sort( { username: 1, date: 1 } ) 查询,username 字段从左边查询命中了创建的索引,但是 date 的查询是升序的,和索引的顺序不匹配,这种索引就命中不到了。

3. 参考资料

给作者打赏,可以加首页微信,咨询作者相关问题!