MongoDB - 索引限制
MongoDB - 索引局限性与考量
Section titled “MongoDB - 索引局限性与考量”尽管索引对查询性能至关重要,但它们并非“银弹”(万能药)。理解其局限性与权衡是构建高效且可伸缩应用程序的关键。本章将探讨在设计索引策略时必须牢记的关键注意事项。
1. 写入性能开销
Section titled “1. 写入性能开销”可以将索引视为一个独立且专门的数据结构。对于在某个已索引集合上执行的每次 insert(插入)、update(更新)或 delete(删除)操作,MongoDB 都必须执行两次写入:一次写入到集合本身,另一次写入到其每个索引。这会引入性能开销。
因此,拥有大量索引的集合将经历较慢的写入速度。这是一个基本的权衡:更快的读取与更慢的写入。关键在于只索引你频繁查询的字段。
对于写密集型工作负载,如日志记录或物联网(IoT)数据摄取,你应特别谨慎地选择索引。
2. 内存与存储考量
Section titled “2. 内存与存储考量”索引会同时占用磁盘存储空间和内存(RAM)。在采用 WiredTiger 存储引擎的现代 MongoDB 版本中,频繁访问的索引和数据会被保存在内存缓存中,以便快速检索。索引的总大小直接影响此缓存。
如果你的索引过大,无法完全舒适地放入 WiredTiger 缓存中,MongoDB 可能需要从磁盘读取它们,这会显著降低速度。你必须确保服务器有足够的 RAM 来支持你的工作集(应用程序频繁访问的数据和索引)。
你可以使用 db.collection.stats() 命令来监控索引大小。
3. 查询选择性与索引使用
Section titled “3. 查询选择性与索引使用”当索引帮助 MongoDB 筛选出一小部分文档时,它的效率最高。某些查询运算符本质上是非选择性的,无法有效利用标准索引:
- 非锚定正则表达式(Un-anchored Regular Expressions): 像
/pattern/这样的正则表达式需要扫描整个索引(或集合)。然而,像/^pattern/这样的左锚定正则表达式 可以 高效地使用该字段上的索引。对于复杂的文本搜索,现代的最佳实践是使用专门的 Atlas Search 索引。 - 否定运算符(
$ne,$not,$nin): 这些运算符通常是非选择性的。一个针对status: { $ne: 'archived' }的查询可能会匹配你 99% 的文档,从而强制进行一次完整的索引扫描。通常更好的做法是设计数据模型,使其查询你 想要 的内容,而不是你 不想要 的内容。 $where子句:$where运算符在服务器端执行任意 JavaScript 代码。它非常强大,但完全无法使用索引,因此在性能关键的查询中应避免使用。
调试提示:验证索引使用情况
Section titled “调试提示:验证索引使用情况”永远不要假设你的查询正在使用索引。务必使用 explain() 方法进行验证。一个获胜计划(winningPlan.stage)将显示 IXSCAN(索引扫描),而一个失败计划则会显示 COLLSCAN(集合扫描)。
db.users.find({ email: ‘test@example.com’ }).explain(‘executionStats’);
4. 索引键限制
Section titled “4. 索引键限制”索引条目的总大小必须小于 1024 字节。此限制适用于索引字段值的 BSON 表示。如果集合中包含违反此限制的现有文档,MongoDB 将拒绝为此集合创建索引。同样,如果索引字段值超过此限制,MongoDB 将拒绝任何新的文档插入或更新。
这通常在索引大型字符串或大型数组字段时成为问题。
5. 最大值与限制
Section titled “5. 最大值与限制”请注意 MongoDB 中的以下硬性限制:
- 每个集合的索引数量: 单个集合最多可以有 64 个索引。
- 复合索引中的字段数量: 复合索引(在多个字段上建立的索引)最多可以包含 32 个字段。
- 索引名称长度: 索引名称的最大长度为 128 个字符(包括命名空间和分隔符)。
常见陷阱与最佳实践
Section titled “常见陷阱与最佳实践”- 过度索引: 不要索引每个字段。这会降低写入速度并浪费内存。分析你的应用程序的查询模式并进行战略性索引。
- 低基数索引: 避免索引具有很少不同值的字段(例如,布尔型
isActive字段)。此类索引的选择性通常不足以发挥作用。 - 忽略复合索引字段顺序: 复合索引中字段的顺序很重要。根据你的查询对其进行排序:1. 相等匹配,2. 排序,3. 范围查询。
- 忘记后台构建: 在生产环境中,始终在后台构建索引(
{ background: true })以避免阻塞数据库操作。在现代 MongoDB 版本中,这已是默认行为。