索引需要覆盖查询的所有部分(相等部分、排序部分和范围部分)。这是因为在典型的find()
查询时,MongoDB只使用一个索引。例如,它通常不会对相等部分使用一个索引,而对排序部分使用另一个索引。
一般来说,索引中字段的顺序需要遵循以下模式相等->排序->范围.
这在中详细描述了优化 MongoDB 复合索引.
对于您的查询,相等部分是tag:..., letterId:...
排序部分是emailId:-1
。您的查询中没有范围部分。
使用这种模式,您需要的复合索引是:
db.test.createIndex({tag:1, letterId:1, emailId:-1})
让我们尝试确认使用该索引可以获得多少性能改进。
测试数据
为了确认索引的适用性,我使用以下命令将 100 万条记录插入到测试数据库中生成js,这是一个使用模板创建随机文档的工具。
根据您的示例,mgeneratejs
我使用的模板是:
$ cat template.json
{
"emailId": "$hash",
"email": "$email",
"letterId": "$hash",
"sendedFrom": "$email",
"resultMsg": "$word",
"owner": "$name",
"created": "$date",
"result": "$bool",
"tag": "$word",
"tryNum": {"$integer": {"min": 0, "max": 1e3}},
"clickHash": "$word",
"links": {"$array": {"of": "$url", "number": {"$integer": {"min": 1, "max": 5}}}}
}
并将 100 万个随机文档导入到 MongoDB 中:
$ mgeneratejs template.json -n 1000000 | mongoimport -d test -c test
测试1:非最优索引
然后,我创建您拥有的索引,并尝试查找不存在的文档,并收集了 10 次查询运行,其中集合仅包含此索引:
> db.test.createIndex({emailId: 1, letterId: 1, result: 1, owner: 1, tag: 1, clickHash: 1})
> db.test.find({"tag" : "xyz", "letterId" : "abc"}).sort({emailId: -1}).limit(1)
Fetched 0 record(s) in 3069ms
Fetched 0 record(s) in 2924ms
Fetched 0 record(s) in 2923ms
Fetched 0 record(s) in 3013ms
Fetched 0 record(s) in 2917ms
Fetched 0 record(s) in 2961ms
Fetched 0 record(s) in 2882ms
Fetched 0 record(s) in 2870ms
Fetched 0 record(s) in 2969ms
Fetched 0 record(s) in 2863ms
因此,使用该索引,查询的响应时间并不长,大多数执行时间接近 3 秒。
测试2:相等->排序->范围索引
通过添加最优相等->排序->范围 index:
> db.test.createIndex({tag:1, letterId:1, emailId:-1})
> db.test.find({"tag" : "xyz", "letterId" : "abc"}).sort({emailId: -1}).limit(1)
Fetched 0 record(s) in 2ms
Fetched 0 record(s) in 1ms
Fetched 0 record(s) in 1ms
Fetched 0 record(s) in 1ms
Fetched 0 record(s) in 1ms
Fetched 0 record(s) in 1ms
Fetched 0 record(s) in 1ms
Fetched 0 record(s) in 1ms
Fetched 0 record(s) in 1ms
Fetched 0 record(s) in 3ms
相比之下,使用最优索引,性能明显提高。超过 3 毫秒内不会返回任何查询,大多数情况下会在 1 毫秒内返回。