管理和减少 MongoDB 磁盘空间使用的最佳实践
MongoDB 是一个流行的 NoSQL 文档数据库,以其灵活性和可扩展性而闻名。然而,如果缺乏主动管理,磁盘空间使用量可能会快速增长,导致性能下降、系统中断和基础设施成本增加。了解 MongoDB 如何消耗磁盘空间并实施有效的管理策略,对于维护健康高效的数据库环境至关重要。
本文深入探讨了管理和减少 MongoDB 磁盘空间的综合策略。我们将探索实用的技术,例如压缩集合、优化和处理大型索引、配置存储引擎设置以提高效率,以及实施数据生命周期策略。遵循这些最佳实践,您可以防止不必要的磁盘增长,确保操作稳定,并延长您的 MongoDB 部署的寿命。
了解 MongoDB 磁盘空间消耗
MongoDB 将磁盘空间用于以下几个组件:
- 数据文件(Data Files):存储集合中实际的 BSON 文档。
- 索引文件(Index Files):存储为支持高效查询执行而创建的 B 树索引。
- 日志文件(Journal Files (WiredTiger)):在写入操作应用于数据文件之前进行记录,确保数据持久性。这些是预先分配的。
- 操作日志(Oplog (Operational Log)):副本集中的特殊固定大小集合,用于记录所有写入操作。对复制至关重要。
- 诊断数据(Diagnostic Data):日志、
mongod进程文件和其他系统相关信息。
随着时间的推移,由于更新、删除和文档增长(填充),集合和索引可能会变得碎片化或包含未使用的已分配空间,从而导致磁盘使用效率低下。即使数据库不再需要这些空间来存放实时数据,操作系统也不会立即回收这些“空白空间”。
减少 MongoDB 磁盘空间的策略
1. 压缩集合和索引
压缩操作通过更有效地重写数据文件和索引文件,帮助回收未使用的磁盘空间。这在大量数据删除或更新后特别有用。
压缩集合
对于 WiredTiger 存储引擎(MongoDB 3.2 之后的默认引擎),compact 主要用于回收已删除文档的可用空间并对集合进行碎片整理。它 不会 像 MMAPv1 的 compact 操作那样从头开始重建集合的数据文件。
db.runCommand({ compact: "myCollection" })
compact 的注意事项:
compact操作会消耗大量资源(CPU、I/O),并且需要相当长的时间,特别是对于大型集合。通常最好在维护窗口期间或在副本集的辅助成员上运行。- 它要求有 等于 被压缩集合大小的可用磁盘空间,因为它会在交换之前在一个新位置重建数据。
- 对于分片集群,请在每个分片上独立运行
compact。
重建索引
索引也可能变得碎片化。重建索引可以回收空间,并有可能提高查询性能。
db.myCollection.reIndex()
reIndex() 的注意事项:
- 从 MongoDB 4.2 开始,
reIndex()是一个在线操作(需要足够的磁盘空间来创建新索引)。对于 4.2 之前的版本,它会对数据库(而不仅仅是集合)施加写锁,从而阻塞所有其他操作。建议首先在辅助成员上运行reIndex(),然后降级主节点,在新主节点上执行此操作。 - 与
compact类似,reIndex()在操作期间需要额外的磁盘空间。
repairDatabase(离线操作)
对于严重的碎片化或数据损坏,repairDatabase 可以重建所有数据文件。这是一个离线操作,需要停止 mongod 实例。
mongod --repair
警告:repairDatabase 应该作为空间回收的最后手段,因为它如果不谨慎处理可能是一个破坏性操作,并且可能需要很长时间。请务必提前进行备份。
2. 优化索引
索引对性能至关重要,但也会消耗大量磁盘空间。未使用或冗余的索引纯粹是开销。
识别和删除不必要的索引
定期审查您的索引,确保它们仍然是必需的。
- 列出集合的所有索引:
javascript db.myCollection.getIndexes() - 监控索引使用情况: 启用数据库性能分析(
db.setProfilingLevel(1))或使用db.collection.stats()来查看索引利用率。云监控工具通常会提供关于索引使用的深入见解。 - 识别重复或冗余的索引: 例如,如果查询可以使用复合索引,则在
{ a: 1, b: 1 }上的索引会使在{ a: 1 }上的索引变得冗余。对于仅涉及a和b的查询,{ a: 1, b: 1, c: 1 }上的索引也会覆盖{ a: 1, b: 1 }上的索引。
一旦确定,删除未使用的索引:
db.myCollection.dropIndex("indexName")
提示:在将删除索引的操作应用于生产环境之前,务必在预演(staging)环境中测试其影响。
使用部分索引(Partial Indexes)
部分索引仅对满足指定筛选表达式的集合中的文档进行索引。这减少了被索引的文档数量,从而节省了磁盘空间并提高了写入性能。
db.orders.createIndex(
{ customerId: 1, orderDate: -1 },
{ partialFilterExpression: { status: "active" } }
)
如果大多数订单是“非活跃”的,此索引将只包含 status 为“active”的文档,从而大幅减小其大小。