MongoDB 释放空闲磁盘空间

MongoDB 释放空闲磁盘空间

当我们从 MongoDB 中删除文档或集合时,MongoDB 并不会将已经占用了的磁盘空间释放,它会一直维护已经占用了磁盘空间的数据文件,尽管数据文件中可能存在大大小小的空记录列表(empty record list)。当客户端程序再次插入文档时,MongoDB 会从空记录列表中分配存储空间给新文档。那么为了更加有效的使用磁盘空间,我们需要对 MongoDB 的数据文件做碎片整理以及未使用空间的回收。思想无非两种:

  1. 对原数据进行重组
  2. 仅将数据复制出来,形成仅数据的完整备份

以下介绍几种常用的实施方法:

  1. compact
  2. db.repairDatabase()
  3. secondary 节点重同步
  4. db.copyDatabase()

一、compact

官网对该命令的定义:对集合中的所有数据和索引进行重写和碎片整理。

使用方法

MongoShell
1
2
replicaset-1:PRIMARY> use yourdatabase;
replicaset-1:PRIMARY> db.runCommand({ compact : 'yourCollection' });

压缩所有集合:
MongoSHell
1
replicaset-1:PRIMARY> db.getCollectionNames().forEach(function (collectionName) {print('Compacting: ' + collectionName);db.runCommand({ compact: collectionName });});

注意事项

  1. 在执行命令前请保证你有比较新的备份
  2. 在使用 MMAPv1 存储引擎的 MongoDB 上 compact 需要数据文件所在分区至少有 2G 的空闲空间。
  3. 在使用 WiredTiger 存储引擎的 MongoDB 上,compact 命令将重写集合和索引,且释放未使用的空间,但使用 MMAPv1 存储引擎的 MongoDB 上,该命令只对集合的数据文件进行碎片整理并重新创建其索引。不会释放空间,在使用 MMAPv1 存储引擎的 MongoDB 上回收空间,建议使用第三种方法 secondary 节点重同步。
  4. 使用 MMAPv1 存储引擎的 MongoDB 中的 Capped Collections,是无法被压缩的,但使用 WiredTiger 存储引擎的 MongoDB 在执行 compact 时会进行压缩。
  5. 副本集上运行该命令时,要分别在每个节点执行。
  6. 该命令只能在 mongod 实例上执行,不能再 mongos 实例上运行。也就是说针对分片集群compact 操作要分别在每个分片节点上执行。
  7. 一般该命令运行在 secondary 节点上,在执行时,会强制节点进入 RECOVERING 状态,RECOVERING 状态的实例读写操作将被阻塞。
  8. 在碰到特殊情况要停止运行该命令时,可通过 db.currentOp() 查询进程信息,然后通过 db.killOp() 干掉进程。
  9. compact 可能会增加数据文件的总大小和数量,尤其是第一次运行时。但这不会增加总集合使用的磁盘空间,因为存储大小是数据库文件中分配的数据量,而不是文件系统上文件的大小/数量。
  10. 使用 MMAPv1 存储引擎的 MongoDB 中的 Capped Collections,是无法被压缩的,但使用 WiredTiger 存储引擎的 MongoDB 在执行 compact 时会进行压缩。

二、db.repairDatabase()

官网该命令的定义:通过丢无效或损坏的数据重建数据库和索引。类似于文件系统修复命令 fsck。所以此命令主要是用于修复数据。

使用方法

MongoShell
1
2
replicaset-1:PRIMARY> use yourdatabase;
replicaset-1:PRIMARY> db.repairDatabase();

注意事项

  1. db.repairDatabase() 主要用于修复数据。若你拥有数据的完整副本,且有权限访问,请使用第三种方法 secondary 节点重同步。
  2. 在执行命令前请保证你有比较新的备份。
  3. 此命令会完全阻塞数据库的读写,谨慎操作
  4. 此命令执行需要数据文件所在位置有等同于所有数据文件大小总和的空闲空间再加上2G
  5. 在使用 MMAPv1 存储引擎的 secondary 节点上执行该命令可以压缩集合数据。
  6. 在使用 WiredTiger 存储引擎的 MongoDB 库上执行不会有压缩的效果
  7. 在碰到特殊情况要停止运行该命令时,可通过 db.currentOp() 查询进程信息,然后通过 db.killOp() 干掉进程。
  8. 非常消耗时间。

    三、secondary

    主要思想就是:删除 secondary 节点中指定数据,使之与 primary 重新开始数据同步。当副本集成员数据太过陈旧,也可以使用重新同步。数据的重新同步与直接复制数据文件不同,MongoDB 会只同步数据因此重同步完成后的数据文件是没有空集合的,以此实现了磁盘空间的回收

使用方法
首先必须确保数据有完整的备份。

  1. 若是 primary 节点,先强制将之变为 secondary 节点,否则跳过此步骤:
    MongoShell
    1
    replicaset-1:PRIMARY> rs.stepdown(120);
  2. 然后在 primary 上删除 secondary 节点:
    MongoShell
    1
    replicaset-1:PRIMARY> rs.remove("IP:port");
  3. 删除 secondary 节点 dbpath 下的所有文件。
  4. 将节点重新加入集群,然后使之自动进行数据的同步:
    MongoShell
    1
    replicaset-1:PRIMARY> rs.add("IP:port");
  5. 等数据同步完成后,循环1-4的步骤可以将集群中所有节点的磁盘空间释放。

针对一些特殊情况,不能下线 secondary 节点的,可以新增一个节点到副本集中,然后 secondary 就自动开始数据的同步了。
总的来说,重同步的方法是比较好的,第一基本不会阻塞副本集的读写,第二消耗的时间相对前两种比较短

四、db.copyDatabase()

MongoDB 还支持在线复制数据:db.copyDatabase("from","to","IP:port"),此种方法也能释放空间,因为 db.copyDatabase 复制的数据,而不是表示在磁盘中的数据文件。但,该命令在4.0版本起被弃用;3.x版本还能继续使用
如:

MongoShell
1
replicaset-1:PRIMARY> db.copyDatabase("sourceDB","DistDB");

将源库 sourceDB。拷贝为 DistDB

当然,该命令支持远程复制。
该命令的完整语法为:

1
db.copyDatabase(<源数据库名称>, <目标数据库名称>, <源mongodb的IP:port>, <源数据库连接需要的账户>,<密码>, <mechanism>)

以上:命令必须在目标数据库服务器上执行。若源数据库与目标数据库存在于一个MongoDB服务器,<源mongodb的IP:port><源数据库连接需要的账户><密码>都可省略。<mechanism> 是身份验证类型,可选的。

注意事项

  1. db.copyDatabase() 不会阻塞源数据库和目标数据库数据的读写,因此可能会出现两份数据不一致的情况
  2. db.copyDatabase() 复制索引数据会锁定数据库,此操作也会对其他数据库产生影响。
  3. db.copyDatabase() 不要在 mongos 实例中使用。
  4. db.copyDatabase() 不要用于复制包含分片集合的数据库。
  5. 在4.0版中更改:db.copyDatabase() 仅支持 SCRAM 进行身份验证 fromhost<mechanism> 选项。
  6. 某些不同版本的 MongoDB 间不支持此种复制方法,详见链接:https://docs.mongodb.com/manual/reference/method/db.copyDatabase/

除此之外,还有一些方法,像使用导入/导出的方法(mongodump/mongorestore),这种方法在数据量非常大的情况是不适用的,因为导入导出的方法使用的全量的形式,要保证有足够的空闲空间来存放导入的数据。

五、备份与还原

通过全量备份与还原数据库回收被 MongoDB 已占用的磁盘空间。

备份

mongodbmp 命令脚本语法如下:

shell
1
mongodump -h dbhost -d dbname -u user -p pwd -o dbdirectory
  • -h:MongDB所在服务器地址,例如:127.0.0.1,当然也可以指定端口号:127.0.0.1:27017
  • -d:需要备份的数据库实例,例如:test
  • -u:数据库用户名,如无则略
  • -p:数据库密码,如无则略
  • -o:备份的数据存放位置,例如:/data/mongobak/,当然该目录需要提前建立,这个目录里面存放该数据库实例的备份数据。

示例:

shell
1
mongodump -h 192.168.1.139:27017 -d akiya -u akiya789 -p akiya_pwd -o /data/mongobak/

执行以上命令后,客户端会连接到ip为 192.168.1.139 端口号为 27017 的 MongoDB 服务上,并备份所有数据到 /data/mongobak/ 目录中。

命令输出结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2020-02-26T15:49:33.791+0800	writing akiya.eventConfig to
2020-02-26T15:49:33.791+0800 writing akiya.microPermissionEntity to
2020-02-26T15:49:33.791+0800 writing akiya.event to
2020-02-26T15:49:33.792+0800 writing akiya.fs.chunks to
2020-02-26T15:49:33.931+0800 done dumping akiya.eventConfig (1371 documents)
2020-02-26T15:49:33.932+0800 writing akiya.fs.files to
2020-02-26T15:49:33.968+0800 done dumping akiya.fs.files (56 documents)
2020-02-26T15:49:33.968+0800 writing akiya.eventGroup to
2020-02-26T15:49:34.216+0800 done dumping akiya.event (217 documents)
2020-02-26T15:49:34.216+0800 writing akiya.listener to
2020-02-26T15:49:34.240+0800 done dumping akiya.microPermissionEntity (506 documents)
2020-02-26T15:49:34.240+0800 writing akiya.mappingAttrEntity to
2020-02-26T15:49:34.241+0800 done dumping akiya.eventGroup (30 documents)
2020-02-26T15:49:34.241+0800 writing akiya.systemHelpEntity to
2020-02-26T15:49:34.269+0800 done dumping akiya.fs.chunks (59 documents)
2020-02-26T15:49:34.270+0800 writing akiya.microServices to
...

数据恢复

MongoDB 使用 mongorestore 命令来恢复备份的数据。

语法

mongorestore命令脚本语法如下:

shell
1
mongorestore -h dbhost -d dbname --dir dbdirectory -u user -p pwd --authenticationDatabase dbname
  • -h:MongoDB 所在服务器地址
  • -d:需要恢复的数据库实例,例如:test,当然这个名称也可以和备份时候的不一样,比如test2
  • —dir:备份数据所在位置,例如:/data/mongobak/akiya
  • -u:认证数据库用户
  • -p:认证数据库密码
  • —authenticationDatabase:指定认证的数据库
  • —drop:mongorestore 恢复数据默认是追加。恢复的时候,先删除当前数据,然后恢复备份的数据。就是说,恢复后,备份后添加修改的数据都会被删除,慎用

创建带认证的业务数据库,如果没有启动密码认证则跳过此步骤;如果在现有数据库上还原也请跳过此步骤。

shell
1
2
3
4
5
6
# mongo admin -u root -p Root_123
replicaset-1:PRIMARY> use admin # 已有用户忽略
replicaset-1:PRIMARY> db.auth("admin","Admin_123") # 已有用户忽略
replicaset-1:PRIMARY> db.auth("root","Root_123") # 已有用户忽略
replicaset-1:PRIMARY> use akiya-restore
replicaset-1:PRIMARY> db.createUser({user:"akiya789",pwd:"akiya_pwd",roles:[{role:"readWrite",db:"akiya-restore"}]})

用户创建完成,可通过下面命令查看:

MongoShell
1
replicaset-1:PRIMARY> show users

修改完成后,只有通过 mongo akiya -u akiya789 -p akiya_pwd 才能登录 akiya789 用户,并查看到集合表信息。

接下来我们执行以下命令:

shell
1
mongorestore -h 192.168.1.139:27017 -d akiya-restore --dir /data/mongobak/akiya/ -u akiya789 -p akiya_pwd --authenticationDatabase akiya-restore

我们可以通过命令 show dbs; 查看备份前与新导入的库的磁盘占用大小,可以看到新导入的库相对占用空间更少。
如果要在现有库上使用备份与恢复的方式进行磁盘空间释放的话,在 mongorestore 时带上 --drop 参数即可。注意:请务必保证在此操作期间不存在数据读写操作

shell
1
mongorestore -h 192.168.1.139:27017 -d akiya --dir /data/mongobak/akiya/ -u akiya789 -p akiya_pwd --authenticationDatabase akiya-restore --drop

参考文章:

评论

:D 一言句子获取中...

加载中,最新评论有1分钟缓存...