

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 具有 3 个以上属性的复合索引
<a name="anti-pattern-compound-index"></a>

## 概述
<a name="compound-index-overview"></a>

复合索引或复合索引在单个索引结构中保留对多个字段的引用。这些索引可优化同时筛选多个字段或将筛选与排序操作相结合的查询的性能。它们对最左边的索引字段的单条件查询也很有效。数据库利用这些索引条目来高效地找到匹配的文档，而无需执行完整的馆藏扫描。

Amazon DocumentDB 可以使用复合索引来支持包含索引字段任何前导子集的查询，这个概念被称为索引前缀。例如，如果您开启了复合索引`{state: 1, city: 1, zipcode: 1}`，Amazon DocumentDB 可以高效地处理仅使用字段 “州”、同时使用 “州” 和 “城市” 字段，或者同时使用三个字段 “州”、“城市” 和 “邮政编码” 的查询。但是，查询必须使用从左到右的字段，不要跳过介于两者之间的任何字段。这意味着仅使用字段 “城市” 或 “邮政编码” 或 “州” 和 “邮政编码” 之类的组合（跳过 “城市”）的查询无法充分利用索引。

在大多数现实场景中，具有三个或更少属性的复合索引通常可以实现最佳性能和资源效率。虽然在综合指数中使用3个以上的属性是可行的，但它通常会导致资源消耗增加，超过收益。

## 对集群的影响
<a name="compound-index-impact"></a>

虽然创建涵盖查询中所有条件的索引以实现最佳理论性能很诱人，但大多数数据筛选都是通过复合索引中的前 1-3 个属性进行的。超过此阈值的其他字段主要影响索引大小，而不是有意义的查询优化。

**存储和 I/O 开销**：具有许多属性的复合索引比更简单的索引占用的存储空间要多得多。大小与索引属性的数量以及索引值本身的大小成正比。

**内存占用**：具有许多属性的复合索引的大量存储空间会在内存中造成相应的占用空间，从而使您的工作集更大，并取代缓冲池中其他经常访问的数据。

**写入操作**：每个影响多个索引字段的文档修改都需要更新整个复合索引条目，乘以完成写入操作所需的工作量。

## 如何识别
<a name="compound-index-identify"></a>

首先查看集合中的所有索引，找出具有三个以上属性的复合索引：

```
// List all indexes for the collection
db.collection.getIndexes()

// Look for indexes with 3+ fields like:

// { "userId": 1, "status": 1, "category": 1, "priority": 1, "region": 1 }
// { "orderId": 1, "customerId": 1, "productId": 1, "timestamp": 1, "warehouse": 1 }
```

## 修复
<a name="compound-index-remediation"></a>

如果使用的是具有三个以上属性的复合索引，请确定使用该索引的查询并寻找优化机会。考虑用包含三个或更少属性的更高效的索引替换这些索引。实现新索引后，删除包含三个以上属性的旧索引。

**注意**  
在删除任何索引之前，请务必与利益相关者协调并验证性能影响。

创建复合索引时，请遵循相等、排序、范围 (ESR) 规则。

相@@ **等排序范围 (ESR) 规则**：这种排序通过首先使用相等条件筛选数据集，然后对简化的集合应用排序操作，最后对尽可能小的子集执行范围扫描，从而最大限度地提高索引效率
+ 将字段排序为相等（完全匹配），
+ 排序（排序），
+ 范围 (>、<、$in)。

```
// Query pattern
db.orders.find({ 
  userId: "user123",                    // Equality
  price: { $gte: 50, $lte: 200 }      // Range
}).sort({ createdAt: -1 })             // Sort

// Optimal index following ESR rule
db.orders.createIndex({ userId: 1, createdAt: -1, price: 1 })
//                      Equality   Sort         Range
```