针对不同的排序过滤条件,应该创建哪些MongoDB个索引来提高性能?
Which MongoDB indexes should be created for different sorting and filtering conditions to improve performance?
我有 MongoDB collection 约 100,000,000 条记录。
在网站上,用户使用 "Refinement search" 功能搜索这些记录,他们可以在其中按多个条件进行筛选:
- 按国家、州、地区;
- 按价格范围;
- 按行业;
此外,他们还可以查看排序的搜索结果:
- 按标题 (asc/desc),
- 按价格 (asc/desc),
- 按最佳匹配字段。
我需要创建索引以避免对上述任何组合进行全扫描(因为用户使用大部分组合)。按照Equality-Sort-Range rule创建索引,我要创建很多索引:
所有过滤器组合×所有排序×所有范围过滤器,如下:
country_title
state_title
region_title
title_price
industry_title
country_title_price
country_industry_title
state_industry_title
...
country_price
state_price
region_price
...
country_bestMatch
state_bestMatch
region_bestMatch
...
实际上,我有更多的标准(包括平等和范围)和更多的排序。例如,我有多个价格字段,用户可以按任何价格排序,因此我必须为每个价格字段创建所有过滤索引,以防用户按该价格排序。
我们使用MongoDB 4.0.9,目前只有一台服务器。
在我进行排序之前,它更容易,至少我可以有一个像 country_state_region
这样的复合索引,并且在搜索一个区域时总是在查询中包含国家和州。但是最后有了排序字段,我不能再这样做了——我必须创建所有不同的索引,即使是位置(country/state/region)和所有排序组合。
此外,并非所有产品都有价格,所以我不能只按 price
字段排序。相反,我必须创建两个索引:{hasPrice: -1, price: 1}
和 {hasPrice: -1, price: -1}
(这里,hasPrice 为 -1,无论价格排序方向如何,hasPrice=true 的记录始终排在第一位)。
目前,我使用 NodeJS 代码生成类似于以下内容的索引(这是简化的示例):
for (const filterFields of getAllCombinationsOf(['country', 'state', 'region', 'industry', 'price'])) {
for (const sortingField of ['name', 'price', 'bestMatch']) {
const index = {
...(_.fromPairs(filterFields.map(x => [x, 1]))),
[sortingField]: 1
};
await collection.ensureIndex(index);
}
}
所以,上面的代码生成了 90 多个索引。而在我的实际任务中,这个数字更多。
是否可以在不降低查询性能的情况下以某种方式减少索引数量?
谢谢!
首先,在MongoDB(参考:https://docs.mongodb.com/manual/reference/limits/)中,单个集合最多可以有64个索引。此外,您永远不应创建 64 个索引,除非没有写入或非常少。
是否可以在不降低查询性能的情况下以某种方式减少索引数量?
在不牺牲功能和查询性能的情况下,您不能。
您可以做的几件事:(假设您使用分页显示结果)
在每一列上创建一个单独的(非复合)索引,并让 MongoDB 执行计划程序根据它具有的元信息(基数、数字等)选择索引。当然,性能会有所下降。
根据您的判断和一些分析,仅为最常使用的组合创建复合索引。
最重要 - 在创建复合索引时,您可以放弃对列进行排序。假设您正在根据行业进行过滤并根据价格进行排序。如果您有复合指数(行业、价格),那么一切都会正常进行。但是,如果您只有行业索引(假设分页结果),那么前几页的查询速度会非常快,但随着您进入下一页,查询速度会不断下降。通常,用户不会在 5-6 页后导航。此外,您必须记住对于较大的跳过值,查询将开始失败,因为排序的 32mb 内存限制。这可以通过启用 allowDiskUse 的聚合(而不是查询)来克服。
检查键集分页(也称为搜索方法)是否可以在您的用例中使用。
我有 MongoDB collection 约 100,000,000 条记录。
在网站上,用户使用 "Refinement search" 功能搜索这些记录,他们可以在其中按多个条件进行筛选:
- 按国家、州、地区;
- 按价格范围;
- 按行业;
此外,他们还可以查看排序的搜索结果:
- 按标题 (asc/desc),
- 按价格 (asc/desc),
- 按最佳匹配字段。
我需要创建索引以避免对上述任何组合进行全扫描(因为用户使用大部分组合)。按照Equality-Sort-Range rule创建索引,我要创建很多索引:
所有过滤器组合×所有排序×所有范围过滤器,如下:
country_title
state_title
region_title
title_price
industry_title
country_title_price
country_industry_title
state_industry_title
...
country_price
state_price
region_price
...
country_bestMatch
state_bestMatch
region_bestMatch
...
实际上,我有更多的标准(包括平等和范围)和更多的排序。例如,我有多个价格字段,用户可以按任何价格排序,因此我必须为每个价格字段创建所有过滤索引,以防用户按该价格排序。
我们使用MongoDB 4.0.9,目前只有一台服务器。
在我进行排序之前,它更容易,至少我可以有一个像 country_state_region
这样的复合索引,并且在搜索一个区域时总是在查询中包含国家和州。但是最后有了排序字段,我不能再这样做了——我必须创建所有不同的索引,即使是位置(country/state/region)和所有排序组合。
此外,并非所有产品都有价格,所以我不能只按 price
字段排序。相反,我必须创建两个索引:{hasPrice: -1, price: 1}
和 {hasPrice: -1, price: -1}
(这里,hasPrice 为 -1,无论价格排序方向如何,hasPrice=true 的记录始终排在第一位)。
目前,我使用 NodeJS 代码生成类似于以下内容的索引(这是简化的示例):
for (const filterFields of getAllCombinationsOf(['country', 'state', 'region', 'industry', 'price'])) {
for (const sortingField of ['name', 'price', 'bestMatch']) {
const index = {
...(_.fromPairs(filterFields.map(x => [x, 1]))),
[sortingField]: 1
};
await collection.ensureIndex(index);
}
}
所以,上面的代码生成了 90 多个索引。而在我的实际任务中,这个数字更多。
是否可以在不降低查询性能的情况下以某种方式减少索引数量?
谢谢!
首先,在MongoDB(参考:https://docs.mongodb.com/manual/reference/limits/)中,单个集合最多可以有64个索引。此外,您永远不应创建 64 个索引,除非没有写入或非常少。
是否可以在不降低查询性能的情况下以某种方式减少索引数量? 在不牺牲功能和查询性能的情况下,您不能。
您可以做的几件事:(假设您使用分页显示结果)
在每一列上创建一个单独的(非复合)索引,并让 MongoDB 执行计划程序根据它具有的元信息(基数、数字等)选择索引。当然,性能会有所下降。
根据您的判断和一些分析,仅为最常使用的组合创建复合索引。
最重要 - 在创建复合索引时,您可以放弃对列进行排序。假设您正在根据行业进行过滤并根据价格进行排序。如果您有复合指数(行业、价格),那么一切都会正常进行。但是,如果您只有行业索引(假设分页结果),那么前几页的查询速度会非常快,但随着您进入下一页,查询速度会不断下降。通常,用户不会在 5-6 页后导航。此外,您必须记住对于较大的跳过值,查询将开始失败,因为排序的 32mb 内存限制。这可以通过启用 allowDiskUse 的聚合(而不是查询)来克服。
检查键集分页(也称为搜索方法)是否可以在您的用例中使用。