Mongodb Delete与TTL索引删除数据磁盘不释放_数据库

简介

     在使用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索引删除数据后无法立即释放磁盘空间的情况下,这些方法也能帮助我们最大限度地优化磁盘空间的利用。