MongoDB 索引进阶

MongoDB 覆盖索引查询

在 MongoDB 中,覆盖索引查询是指查询只使用索引来满足查询条件,并且不需要访问实际的文档数据。这种查询方式可以提高查询性能,因为它避免了读取和传输额外的文档数据。

要实现覆盖索引查询,需要满足以下条件:

(1)创建一个包含查询条件和需要返回的字段的索引,确保索引覆盖了查询条件和需要返回的字段。

(2)在查询时使用投影操作符(projection operator)来指定只返回需要的字段,而不是整个文档。

以下是一个示例,演示如何执行覆盖索引查询:

(1)准备一个集合,集合拥有如下数据:

> db.col.find()
{ "_id" : ObjectId("64e71af810366fa87109a12f"), "name" : "张三", "age" : 3, "email" : "zhangsan@outlook.com", "sex" : "male" }
{ "_id" : ObjectId("64e71af810366fa87109a130"), "name" : "李四", "age" : 22, "email" : "lisi@qq.com", "sex" : "female" }
{ "_id" : ObjectId("64e71af810366fa87109a131"), "name" : "王五", "age" : 27, "email" : "wangwu@sina.com.cn", "sex" : "male" }
{ "_id" : ObjectId("64e71af810366fa87109a132"), "name" : "赵六", "age" : 22, "email" : "zhaoliu@gmail.com", "sex" : "female" }
{ "_id" : ObjectId("64e71af810366fa87109a133"), "name" : "顾七", "age" : 30, "email" : "guqi@qq.com", "sex" : "male" }
{ "_id" : ObjectId("64e71af810366fa87109a134"), "name" : "何八", "age" : 42, "email" : "heba@outlook.com", "sex" : "male" }

(2)创建索引:

> db.col.createIndex({ name: 1, age: 1});
{
       "numIndexesBefore" : 1,
       "numIndexesAfter" : 2,
       "createdCollectionAutomatically" : false,
       "ok" : 1
}

在上面的示例创建了一个索引,包含字段 name 和 age,而这两个字段是查询条件和需要返回的字段。

注意:5.0 之前版本可以使用 db.collection.ensureIndex() ,但 ensureIndex() 在 5.0 版本后已被移除,使用 createIndex() 代替。

(3)执行覆盖索引查询:

> db.col.find({ name: "张三" }, { name: 1, age:1, _id: 0 })
{ "name" : "张三", "age" : 3 }

在上面的示例中,name 是查询条件,name 和 age 是需要返回的字段。_id: 0 用于排除默认情况下返回的 _id 字段。

(4)使用 explain 确认是否使用了索引:

> db.col.find({ name: "张三" }, { name: 1, age:1, _id: 0 }).explain()
{
       "explainVersion" : "1",
       "queryPlanner" : {
               ...
               "winningPlan" : {
                       ...
                       "inputStage" : {
                               "stage" : "IXSCAN", 这里表示使用了索引
                               "keyPattern" : {
                                       "name" : 1,
                                       "age" : 1
                               },
                               "indexName" : "name_1_age_1",
...

> db.col.find().explain()
{
       "explainVersion" : "1",
       "queryPlanner" : {
               ...
               "winningPlan" : {
                       "stage" : "COLLSCAN", 全表扫描,没有使用索引
                       "direction" : "forward"
               },
...

在执行覆盖索引查询时,MongoDB 将只使用索引来满足查询条件,并且只返回指定的字段,而不需要访问实际的文档数据。这可以大大提高查询性能,尤其是当需要返回的字段很大或者集合中的文档非常庞大时。

注意:覆盖索引查询适用于只需要查询指定字段的情况。如果需要查询的字段不在索引中,或者需要返回整个文档,那么覆盖索引查询将无效,MongoDB 将仍然需要访问实际的文档数据。

索引数组字段

在 MongoDB 中,可以对数组字段创建索引。这样可以提高对数组字段的查询性能。当对数组字段创建索引时,MongoDB 会为每个数组元素创建一个索引条目。

以下是一个示例,演示如何对数组字段创建索引:

(1)创建包含数组字段的集合:

> db.array_demo.insertOne({
...   name: "John",
...   hobbies: ["reading", "gaming", "coding"]
... });
{
       "acknowledged" : true,
       "insertedId" : ObjectId("64f68be0e851871a0dba078f")
}

在上面的示例中,hobbies 是一个包含多个元素的数组字段。

(2)创建索引:

> db.array_demo.createIndex({ hobbies: 1 });
{
       "numIndexesBefore" : 1,
       "numIndexesAfter" : 2,
       "createdCollectionAutomatically" : false,
       "ok" : 1
}

在上面的示例中,hobbies 是要创建索引的数组字段。

(3)执行数组字段的查询:

> db.array_demo.find({ hobbies: "reading" });
{ "_id" : ObjectId("64f68be0e851871a0dba078f"), "name" : "John", "hobbies" : [ "reading", "gaming", "coding" ] }

在上面的示例中,通过查询数组字段 hobbies 为 “reading”,可以利用数组字段的索引来提高查询性能。

(4)使用 explain 查看查询是否使用索引:

> db.array_demo.find({ hobbies: "reading" }).explain()
{
       "queryPlanner" : {
               ...
               "winningPlan" : {
                       "stage" : "FETCH",
                       "inputStage" : {
                               "stage" : "IXSCAN", 使用了索引
                               "keyPattern" : {
                                       "hobbies" : 1
                               },
                               "indexName" : "hobbies_1",
...

注意,当对数组字段创建索引时,MongoDB 会为每个数组元素创建一个索引条目。这意味着索引的大小会随着数组元素的数量增加而增加。如果数组字段中的元素数量非常大,可能会导致索引变得庞大,从而影响性能和存储空间。

此外,对于数组字段的查询,MongoDB 提供了一些特殊的操作符,如 $elemMatch 用于匹配数组中满足指定条件的元素。这些操作符可以进一步优化对数组字段的查询。

索引子文档字段

在 MongoDB 中,可以对子文档的字段创建索引。子文档是指嵌套在文档中的文档。

以下是一个示例,演示如何对子文档字段创建索引:

(1)创建包含子文档的集合:

> db.sub_demo.insertOne({
...   name: "John",
...   address: {
...     city: "New York",
...     state: "NY",
...     country: "USA"
...   }
... });
{
       "acknowledged" : true,
       "insertedId" : ObjectId("64f695bde851871a0dba0790")
}

在上面的示例中,address 是一个子文档字段,包含了城市、州和国家信息。

(2)创建索引:

> db.sub_demo.createIndex({ "address.city": 1 });
{
       "numIndexesBefore" : 1,
       "numIndexesAfter" : 2,
       "createdCollectionAutomatically" : false,
       "ok" : 1
}

在上面的示例中,address.city 是要创建索引的子文档字段。

(3)执行子文档字段的查询:

> db.sub_demo.find({ "address.city": "New York" });
{ "_id" : ObjectId("64f695bde851871a0dba0790"), "name" : "John", "address" : { "city" : "New York", "state" : "NY", "country" : "USA" } }

在上面的示例中,通过查询子文档字段 address.city 为 “New York”,可以利用子文档字段的索引来提高查询性能。

(4)通过 explain 查看查询是否使用索引:

> db.sub_demo.find({ "address.city": "New York" }).explain()
{
       "explainVersion" : "1",
       "queryPlanner" : {
               ...
               "winningPlan" : {
                       "stage" : "FETCH",
                       "inputStage" : {
                               "stage" : "IXSCAN", 使用了索引
                               "keyPattern" : {
                                       "address.city" : 1
                               },
                               "indexName" : "address.city_1",
...

注意,当对子文档字段创建索引时,MongoDB 会为子文档的每个字段创建索引条目。这意味着索引的大小会随着子文档字段的数量增加而增加。如果子文档字段中的字段数量非常大,可能会导致索引变得庞大,从而影响性能和存储空间。

此外,对于子文档字段的查询,可以使用点符号(.)来指定子文档字段的路径。可以在查询中使用多个子文档字段来进行更复杂的查询。

MongoDB 索引限制

额外开销

每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。所以,如果你很少对集合进行读取操作,建议不使用索引。

内存(RAM)使用

由于索引是存储在内存 (RAM)中,你应该确保该索引的大小不超过内存的限制。如果索引的大小大于内存的限制,MongoDB 会删除一些索引,这将导致性能下降。

查询限制

不能被以下的查询使用:

  • 正则表达式及非操作符,如 $nin, $not, 等。

  • 算术运算符,如 $mod, 等。

  • $where 子句

所以,检测你的语句是否使用索引是一个好的习惯,可以用 explain 来查看。

索引键限制

从 2.6 版本开始,如果现有的索引字段的值超过索引键的限制,MongoDB 中不会创建索引。

插入文档超过索引键限制

如果文档的索引字段值超过了索引键的限制,MongoDB 不会将任何文档转换成索引的集合。与 mongorestore 和 mongoimport 工具类似。

最大范围

集合中索引不能超过64个

索引名的长度不能超过128个字符

一个复合索引最多可以有31个字段

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号