在 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 会为子文档的每个字段创建索引条目。这意味着索引的大小会随着子文档字段的数量增加而增加。如果子文档字段中的字段数量非常大,可能会导致索引变得庞大,从而影响性能和存储空间。
此外,对于子文档字段的查询,可以使用点符号(.)来指定子文档字段的路径。可以在查询中使用多个子文档字段来进行更复杂的查询。
每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。所以,如果你很少对集合进行读取操作,建议不使用索引。
由于索引是存储在内存 (RAM)中,你应该确保该索引的大小不超过内存的限制。如果索引的大小大于内存的限制,MongoDB 会删除一些索引,这将导致性能下降。
不能被以下的查询使用:
正则表达式及非操作符,如 $nin, $not, 等。
算术运算符,如 $mod, 等。
$where 子句
所以,检测你的语句是否使用索引是一个好的习惯,可以用 explain 来查看。
从 2.6 版本开始,如果现有的索引字段的值超过索引键的限制,MongoDB 中不会创建索引。
如果文档的索引字段值超过了索引键的限制,MongoDB 不会将任何文档转换成索引的集合。与 mongorestore 和 mongoimport 工具类似。
集合中索引不能超过64个
索引名的长度不能超过128个字符
一个复合索引最多可以有31个字段