在回答这个问题之前,先了解一下 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/