MongoDB 文档被delete后,如何回收磁盘空间?

2024-07-18 15:18:27 浏览数 (2)

在回答这个问题之前,先了解一下 MySQL InnoDB 存储引擎是如何回收磁盘空间的。

假设有一张表包含 1000 万条记录,删除其中的 800 万条记录,此时 InnoDB 的磁盘空间不会自动释放。要回收磁盘空间,需要执行以下命令:

代码语言:sql复制
ALTER TABLE tableName ENGINE=InnoDB;

MongoDB 的 WiredTiger 存储引擎也有类似的行为。要回收磁盘空间,可以执行以下命令:

代码语言:javascript复制
use dbName
// 在副本集 primary 上执行需要加 force 选项
db.runCommand({compact: "collectionName", force: true})

下面通过一个测试用例来实际演示这一过程。

首先,插入 100 万条随机生日数据到 MongoDB 的 demo 集合中。

代码语言:javascript复制
function getRandomDate() {
    const start = new Date(1950, 0, 1);
    const end = new Date(2005, 11, 31);
    return new Date(start.getTime()   Math.random() * (end.getTime() - start.getTime()));
}

for (var i = 1; i <= 1000000; i  ) {
    db.demo.insert({
        name: "name"   i,
        birthday: getRandomDate()
    });
    if (i % 10000 === 0) {
        print(`Inserted ${i} records`);
    }
}

print("Insertion complete");

接下来,删除所有生日在 1980 年 1 月 1 日之前的记录。

代码语言:javascript复制
db.demo.count({ birthday: { $lt: new Date("1980-01-01") } });
db.demo.deleteMany({ birthday: { $lt: new Date("1980-01-01") } });

删除操作后,检查 demo 集合可以回收的磁盘空间(单位:字节)。

代码语言:javascript复制
db.demo.stats().wiredTiger["block-manager"]["file bytes available for reuse"];

最后,执行磁盘空间回收操作。

代码语言:javascript复制
// 在副本集 primary 上执行需要加 force 选项
db.runCommand({compact: "demo", force: true});

需要注意的是,在 MongoDB 4.4 版本之前,compact 操作会阻塞 CRUD 操作。而在 MongoDB 4.4 及更新的版本中,compact 操作不会阻塞 CRUD 操作,但会阻塞元数据操作,例如删除集合、删除索引和创建新索引。

最后附上统计库里每个集合可回收的空间(GB)脚本

代码语言:javascript复制
// 使用指定的数据库
use yourDB

// 定义常量
var bytesInGB = 1024 * 1024 * 1024;

// 获取所有集合名称
var cols = db.getCollectionNames();

// 创建一个对象来存储每个集合的可回收空间
var collectionSpace = {};

// 计算总可回收空间
var totalReclaimableSpace = 0;

// 遍历每个集合
cols.forEach(function(col) {
    if(col != 'system.profile') {
        var stats = db[col].stats();
        var reclaimableSpace = stats.wiredTiger["block-manager"]["file bytes available for reuse"];
        
        // 将字节转换为GB并保留两位小数
        var spaceInGB = (reclaimableSpace / bytesInGB).toFixed(2);
        
        // 存储每个集合的可回收空间
        collectionSpace[col] = spaceInGB;
        
        // 累加总可回收空间
        totalReclaimableSpace  = reclaimableSpace;
    }
});

// 打印每个集合的可回收空间
print("每个集合可回收的空间(GB):");
for (var col in collectionSpace) {
    print("集合:"   col   " -> 可回收空间(GB): "   collectionSpace[col]);
}

// 打印总可回收空间
print("n总共可回收空间(GB): "   (totalReclaimableSpace / bytesInGB).toFixed(2));

参考文献:

https://www.percona.com/blog/using-compact-in-percona-server-for-mongodb-from-version-4-4/

https://www.percona.com/blog/how-to-reclaim-disk-space-in-percona-server-for-mongodb/

0 人点赞