索引
Overview
索引是 MongoDB 中为高效查询提供支持的数据结构。它们包含文档中部分数据的副本,以提高查询效率。
如果没有索引,MongoDB 必须扫描集合中的每份文档,从而找到与每个查询相匹配的文档。这些集合扫描很慢,可能会对应用程序的性能产生负面影响。使用索引来限制 MongoDB 扫描的文档数量,可以提高查询效率,促进返回速度。
查询覆盖和性能
当您对 MongoDB 执行查询时,您的查询可以包括以下三个部分:
查询条件,用于指定您要查找的字段和值
影响查询执行的选项,例如读关注
用于指定 MongoDB 应返回字段的投影条件(可选)
当查询条件和查询的投影中指定的所有字段都已经被索引,MongoDB 会直接从索引返回结果,而无需扫描集合中的任何文档,也不需要将这些文档加载到内存中。
操作注意事项
为了提高查询性能,请对您应用程序查询操作中的常见字段,以及其他能返回已排序结果的操作中的常见字段建立索引。您添加的每个索引在活动状态下都会占用一定磁盘空间和内存,因此您应当跟踪索引的内存和磁盘使用情况,以便进行容量规划。此外,当写入操作更新了一个被索引的字段时,MongoDB 也必须更新相关索引。
有关设计数据模型和选择适合应用程序的索引的更多信息,请参阅有关“索引策略”和“数据建模和索引”的 MongoDB 服务器文档。
索引类型
MongoDB 支持多种不同的索引类型来进行数据查询。以下部分描述了最常用的索引类型,并提供了创建每种类型索引的示例代码。
单字段索引
单字段索引是指可提高特定查询的性能的索引,而此类查询会对文档的单个字段指定升序或降序排序顺序。
以下示例使用createIndex()
title
movies
方法在sample_mflix
数据库的collection集合的字段字段上创建升序索引。
const database = client.db("sample_mflix"); const movies = database.collection("movies"); // Create an ascending index on the "title" field in the // "movies" collection. const result = await movies.createIndex({ title: 1 }); console.log(`Index created: ${result}`);
以下是一个使用上述所建索引实现高效查询的示例。
const query = { title: "Batman" } const sort = { title: 1 }; const projection = { _id: 0, title: 1 }; const cursor = movies .find(query) .sort(sort) .project(projection);
如要了解更多信息,请参阅单字段索引。
复合索引
复合索引可提高为文档的多个字段指定升序或降序排序顺序的查询的性能。您必须为索引中的每个字段指定方向(升序或降序)。
以下示例使用 createIndex()
方法在 sample_mflix
数据库的 movies
集合中的 type
和 genre
字段上创建复合索引。
const database = client.db("sample_mflix"); const movies = database.collection("movies"); // Create an ascending index on the "type" and "genre" fields // in the "movies" collection. const result = await movies.createIndex({ type: 1, genre: 1 }); console.log(`Index created: ${result}`);
以下是一个使用上述所建索引实现高效查询的示例。
const query = { type: "movie", genre: "Drama" }; const sort = { type: 1, genre: 1 }; const projection = { _id: 0, type: 1, genre: 1 }; const cursor = movies .find(query) .sort(sort) .project(projection);
要了解更多信息,请参阅复合索引。
多键索引(数组字段索引)
多键索引可提高对包含数组值的字段的查询性能。
您可以通过调用 createIndex()
方法,对具有数组值的字段创建多键索引。以下代码将对 sample_mflix
数据库 movies
集合中的 cast
字段创建升序索引:
const database = client.db("sample_mflix"); const movies = database.collection("movies"); // Create a multikey index on the "cast" field const result = await movies.createIndex({ cast: 1 });
以下代码会查询多键索引以查找 cast
字段值包含“Viola Davis”的文档:
const query = { cast: "Viola Davis" }; const projection = { _id: 0, cast: 1 , title: 1 }; const cursor = movies .find(query) .project(projection);
在查询覆盖、索引绑定计算和排序行为方面,多键索引的行为与非多键索引不同。有关多键索引的完整解释,包括对其行为和限制的讨论,请参阅 MongoDB Server 手册中的多键索引页面。
聚集索引
聚集索引是指可提高针对集群化集合的插入、更新和删除操作的性能的索引。集群化集合可存储按聚集索引键值排序的文档。
要创建聚集索引,请在 CollectionOption
中指定 clusteredIndex
选项。clusteredIndex
选项必须将 _id
字段指定为键,并将唯一字段指定为 true
。
以下示例使用 createCollection()
方法在 tea
数据库的 vendors
集合中的 _id
字段上创建聚集索引。
const db = client.db('tea'); await db.createCollection('ratings', { clusteredIndex: { key: { _id: 1 }, unique: true } });
Text Indexes
文本索引支持对字符串内容进行文本搜索查询。这些索引可以包括任何值为字符串或字符串元素数组的字段。
MongoDB 支持多种语言的文本搜索,因此在创建索引时,有选项可供您指定默认语言。您还可以指定一个权重选项,来优先处理索引中的某些文本字段。权重表示这些字段相对于其他索引字段的重要性。
要了解有关文本搜索的更多信息,请参阅 文本搜索查询指南。
以下示例使用createIndex()
方法执行以下操作:
在
blogPosts
集合中的title
和body
字段上创建一个text
索引将
english
指定为默认语言将
body
的字段权重设为10
,并将title
的字段权重设为3
// Get the database and collection on which to create the index const myDB = client.db("testDB"); const myColl = myDB.collection("blogPosts"); // Create a text index on the "title" and "body" fields const result = await myColl.createIndex( { title: "text", body: "text" }, { default_language: "english" }, { weights: { body: 10, title: 3 } } );
以下查询使用了在上述代码中创建的文本索引:
// Query for documents where body or title contain "life ahead" const query = { $text: { $search: "life ahead" } }; // Show only the title field const projection = { _id: 0, title: 1 }; // Execute the find operation const cursor = myColl.find(query).project(projection);
要了解有关文本索引的更多信息,请参阅服务器手册中的文本索引。
地理空间索引
MongoDB 支持使用 2dsphere 索引查询地理空间坐标数据。借助 2dsphere 索引,您可以针对地理空间数据进行包含、相交和邻近范围方面的查询。有关使用 MongoDB Node.js 驱动程序查询地理空间数据的更多信息,请阅读搜索地理空间指南。
要创建2 dsphere 索引,您必须指定仅包含GeoJSON 对象的字段。 有关此类型的更多详细信息,请参阅有关GeoJSON 对象的 MongoDB 服务器手册页面。
以下样本文档中的 location.geo
字段来自 sample_mflix
数据库中的 theaters
集合,是一个描述影院坐标的 GeoJSON 点对象:
{ "_id" : ObjectId("59a47286cfa9a3a73e51e75c"), "theaterId" : 104, "location" : { "address" : { "street1" : "5000 W 147th St", "city" : "Hawthorne", "state" : "CA", "zipcode" : "90250" }, "geo" : { "type" : "Point", "coordinates" : [ -118.36559, 33.897167 ] } } }
以下示例使用 createIndexes()
方法对 sample_mflix
数据库的 theaters
集合的 location.geo
字段创建 2dsphere
索引,以启用地理空间搜索。
const database = client.db("sample_mflix"); const movies = database.collection("movies"); // Create a 2dsphere index on the "location.geo" field in the "theaters" collection. const result = await movies.createIndex({ "location.geo": "2dsphere" }); console.log(`Index created: ${result}`);
MongoDB 还支持 2d
索引,用于计算欧几里德平面上的距离,以及处理 MongoDB 2.2 及更早版本中使用的“旧版坐标对”语法。要了解更多信息,请参阅地理空间查询。
Unique Indexes
唯一索引可确保索引字段不存储重复值。默认情况下,MongoDB 在创建集合期间会在 _id
字段上创建唯一索引。要创建唯一索引,请指定要防止重复的字段或字段组合,并将 unique
选项设置为 true
。
以下示例使用 createIndex()
方法在 sample_mflix
数据库 theaters
集合中的 theaterId
字段上创建唯一索引。
const database = client.db("sample_mflix"); const movies = database.collection("movies"); // Create a unique index on the "theaterId" field in the "theaters" collection. const result = await movies.createIndex({ theaterId: 1 }, { unique: true }); console.log(`Index created: ${result}`);
如果您的写操作违反了唯一索引的规则,即尝试对索引字段存储重复的值, MongoDB 将抛出类似以下的报错:
E11000 duplicate key error index
要了解详情,请参阅唯一索引。
列出索引
您可以使用listIndexes()
方法列出集合的所有索引。 listIndexes() 方法接受一个可选的 ListIndexesOptions 参数。listIndexes()
方法返回 ListIndexesCursor 类型的对象。
以下代码使用 listIndexes()
方法列出集合中的所有索引:
const result = await collection.listIndexes().toArray(); console.log("Existing indexes:\n"); for(const doc in result){ console.log(doc); }
搜索索引列表
连接到MongoDB Server7.0 或更高版本时,您可以使用新的 listSearchIndexes() 方法返回包含给定集合的搜索索引的游标。listSearchIndexes()
方法接受一个可选的string参数 name
,以便仅返回具有匹配索引名称的索引。 它还带有可选的 aggregateOptions 参数。
以下代码使用listSearchIndexes()
方法列出集合中的所有搜索索引:
const result = await collection.listSearchIndexes().toArray(); console.log("Existing search indexes:\n"); for(const doc in result){ console.log(doc); }