简介
在使用MongoDB时,我们经常需要删除过期或不再需要的数据,以保证数据库的性能和存储效率。MongoDB提供了多种删除数据的方法,包括普通的删除操作(delete)和TTL(Time-To-Live)索引。虽然这两种方法都能从逻辑上删除数据,但它们在磁盘空间管理上的表现却并不相同。
删除操作的基本原理
无论是通过delete命令还是TTL索引,MongoDB删除数据的过程都包括以下几个步骤:
- 逻辑删除:被删除的文档从集合中移除,不再出现在查询结果中。
- 标记空间可重用:被删除文档的存储空间被标记为可重用,但物理空间并未立即释放。
- 后台压缩:WiredTiger存储引擎会在某些情况下自动压缩数据文件,回收已删除文档的空间,但这个过程是渐进的,不会立即完成。
普通删除操作(Delete)
通过delete命令删除文档时,例如:
db.collection.deleteOne({ field: "value" });
被删除的文档从集合中移除,但其占用的磁盘空间并未立即释放。WiredTiger存储引擎会将这些空间标记为可重用,并在将来的写操作中重新使用这些空间。此机制有助于提高写入效率,但可能导致磁盘空间的利用率不理想。
TTL索引删除
TTL索引允许我们为文档设置过期时间,MongoDB会定期扫描集合,并自动删除已过期的文档。创建TTL索引的示例如下:
db.collection.createIndex({ "expireAt": 1 }, { expireAfterSeconds: 0 });
与普通删除操作类似,TTL索引删除的文档也不会立即释放其占用的磁盘空间,而是被标记为可重用。尽管TTL索引自动管理过期数据,避免手动删除的繁琐,但同样无法立即回收磁盘空间。
磁盘碎片由来
在删除MongoDB实例的数据后,这些被删除数据使用的存储空间会被标记为空闲,随后写入的新数据可能会被直接存储到这部分空闲的存储空间中,也可能会先扩展文件的存储空间再存储到文件末尾。上述情况将导致一部分空闲的存储空间不会被使用,这些未被使用的空闲存储空间被称之为磁盘碎片,磁盘碎片越多,磁盘利用率就越低。
查看碎片方法
//无碎片集合
db.tmp0425.stats().wiredTiger["block-manager"]["file bytes available for reuse"]
0
//有碎片集合 18GB
db.el_frequent_visitor_tmp0425.stats().wiredTiger["block-manager"]["file bytes available for reuse"]
20629598208
手动回收磁盘空间的方法
为了确保删除操作后磁盘空间的有效利用,我们可以采用以下几种手动回收磁盘空间的方法:
Compact 命令
compact 命令对指定的集合进行压缩和整理,尝试回收未使用的空间。此操作可能会影响性能,并且在操作期间集合会被锁定。
db.runCommand(
{
compact: <collection name>
}
)
以下是对不同架构,执行操作流程:
1、MongoDB4.4- 7.0 副本集、集群分片
- 优先在从节点上运行 compact,逐个处理每个从节点。
- 重新分配主节点,使当前主节点降级,并选出新的主节点。
- 压缩旧主节点,在其成为从节点后执行 compact 命令。
- 如果必须在主节点上运行 compact 命令,可以使用 force 选项来强制执行;
2、MongoDB 4.4以前副本集、集群分片
- compact 命令只会阻塞正在进行压缩操作的数据库,不会影响同一个 MongoDB 实例中其他数据库的操作。
- 优先在从节点上运行 compact,逐个处理每个从节点。
- 重新分配主节点,使当前主节点降级,并选出新的主节点。
- 压缩旧主节点,在其成为从节点后执行 compact 命令。
- 如果必须在主节点上运行 compact 命令,可以使用 force 选项来强制执行;
3、不同版本之间从节点限制
MongoDB 4.4 之前:
- 阻塞行为:compact 命令会阻塞从节点上的所有读写活动。
- 复制状态:在执行 compact 命令期间,从节点进入 RECOVERING 状态,无法进行复制。
MongoDB 4.4:
- 阻塞行为:compact 命令不会阻塞读取操作,但会阻塞复制。
- 复制状态:从节点不再进入 RECOVERING 状态。
MongoDB 4.4.17 之后:
- 阻塞行为:从节点可以在运行 compact 命令时继续复制数据。
- 读取操作:允许在执行 compact 命令期间进行读取操作。
4、注意事项
- MongoDB 4.2
compact 命令只会阻塞正在进行压缩操作的数据库,不会影响同一个 MongoDB 实例中其他数据库的操作。 - MongoDB 4.4、4.4.17、7.0阻塞
db.collection.drop()
db.collection.createIndex()
db.collection.createIndexes()
db.collection.dropIndex()
db.collection.dropIndexes()
collMod - MongoDB 4.4、4.4.17、7.0允许
读取操作(如 find 查询。
写入操作(如 insert、update、delete)
重建索引
ReIndex 命令通过删除集合上的所有索引并重新创建它们,可以在一定程度上帮助减少磁盘碎片。这是因为重新创建索引时,索引数据会被重新写入磁盘,以一种更连续、更有序的方式存储。
db.collection.reIndex();
具体过程
删除现有索引: reIndex 命令首先删除集合上的所有索引。
重新创建索引: 然后重新根据集合中的数据创建这些索引。
通过重新创建索引,索引数据在磁盘上的布局会变得更加连续,从而减少碎片。这可以提高查询性能和存储效率。
注意事项
- 性能开销: 重新创建索引是一个耗时且资源密集型的操作,尤其是在数据量大或索引多的情况下。因此,在执行 reIndex 时,可能会对数据库性能产生显著影响。
- 只适用于独立实例: reIndex 只能在独立实例(standalone instances)上运行,而不能在副本集(replica sets)或分片集群(sharded clusters)上运行。
- 替代方法: 对于需要优化存储和减少磁盘碎片的用户,可以考虑其他方法,例如定期重建集合或者使用 MongoDB 提供的压缩选项。
- reIndex: 从 MongoDB 6.0 版本开始,reIndex 命令已经被弃用。尝试运行该命令时,会在日志中记录一条警告信息。
导出和导入数据
通过 mongodump 和 mongorestore 命令将数据导出到一个文件,然后删除旧数据文件,再将数据重新导入。这个过程确保所有未使用的空间被完全回收。
mongodump --db yourDatabase --out /path/to/backup
mongorestore --drop --db yourDatabase /path/to/backup/yourDatabase
最佳实践
为了优化磁盘空间的使用,我们建议以下最佳实践:
定期维护:定期执行 compact 和重建索引操作,保持数据文件紧凑。
监控和预警:使用 mongostat 和 mongotop 等监控工具设置预警,及时发现和处理磁盘空间不足的问题。
合理配置TTL:根据实际需求设置TTL时间,平衡数据保留时间和存储需求。
通过理解和应用这些磁盘管理机制和最佳实践,我们可以更有效地利用MongoDB的存储资源,确保数据库的高效运行。即使在使用delete和TTL索引删除数据后无法立即释放磁盘空间的情况下,这些方法也能帮助我们最大限度地优化磁盘空间的利用。