Bootstrap

MongoDB磁盘清理那些事儿

作者:技术支持T2团队 郝鹏

摘要

删除了业务数据,但是磁盘空间不释放,是整理磁盘,还是copy数据,怎么破?通过本文介绍加上自己业务实际情况即可得到答案。

业务背景

我们是一家做AI语音质检业务的公司,大部分客户采用私有化部署,项目中主要业务数据存储DB为MongoDB(v3.6.12,v4.0.2),录音文件存放在OSS或者基于MongoDB的GridFS分布式文件存储系统。私服录音文件存储主要采用MongoDB的GridFS副本集方案,它基于MongoDB的文件系统,便于管理和扩展。GridFS 会将大文件对象分割成多个小的chunk(文件片段),一般为256k/块,每块chunk将作为MongoDB的一个文档(document)被存储在chunks集合中。

问题描述

私服客户业务量的增加,录音数据不断增加,面临着磁盘空间不足的情况,因为MongoDB的特性,直接删除数据占用的磁盘空间并不会释放,即使drop collection也不行,除非drop database。如果一个db曾经有大量的数据一段时间后又删除的话,硬盘空间就是一个问题,如何收回被mongodb占用的多余空间?磁盘空间越来越大,为了避免磁盘空间超过95%以上导致锁库,清理方案迫在眉睫。

思考问题的解决方向

参考官方的磁盘整理还是找一块更大的磁盘导出再导入数据靠谱?

提前准备(+必须)

1. 客户允许的操作时间:48小时

周六日执行😭,不能影响工作日使用。

2. 目前磁盘大小:3.8T

3. 目前MongoDB占用磁盘空间大小:3.4T

4. delete掉需要清楚的数据

5. 预估能整理的磁盘大小:

db.call_audio.chunks.stats().wiredTiger["block-manager"]["file bytes available for reuse"]

>>1795354353454

byte (B)换算为TB后为1.63287TB

6. MongoDB的版本:

3.6.12, 4.0.2

7. MongoDB的引擎:WiredTiger

使用MMAPv1存储引擎的MongoDB中的Capped Collections,是无法被压缩的,但使用WiredTiger存储引擎的MongoDB在执行compact时会进行压缩

8. 所有的collections的索引备份:

这一步非常重要,对于业务关键索引一定不能丢

9. 源数据备份:

需要协调一块更大的磁盘作为备份用,基于一主一从一备份的原则要了一块(3*目前MongoDB占用磁盘空间)大小 12T左右的磁盘

 

解决方法选型

方法一:官方推荐compact整理(推荐指数5颗星)

操作步骤:

0.连接MongoDB 节点
1.use '业务DB' //切换到业务库
2.查看预估可以整理的磁盘碎片大小
db.call_audio.chunks.stats().wiredTiger["block-manager"]["file bytes available for reuse"]
>>1795354353454
3.进行碎片整理
db.runCommand({compact:"",force:true})
说明:
(:集合名。您可以通过show tables命令查询现有的集合。
force为可选项,如您需要在副本集实例的Primary节点执行该命令,需要设置force的值为true。)
4.等待执行,返回{ "ok" : 1 }代表执行完成。
说明:
(如果您执行的目标是一个大集合,而compact命令马上就返回{ "ok" : 1 }集合的物理空间也没有任何变化,则表示该集合没有必要进行碎片整理。
compact操作不会传递给Secondary节点,当实例为副本集实例时,请重复上述步骤通过MongoDB shell连接至Secondary节点,执行碎片整理命令。)

5.验证磁盘空间有没有变化

注意点:

当然以上是我们期望的多么完美的运行过程!!!
注意点:
1.执行前一定要停掉业务系统,确保没有读写操作,否则会导致锁住
2.大部分情况下磁盘慢场景下,都不满足磁盘整理的条件,所以操作后很快就返回了,如果删除了很多数据,并且还有不小于10%-20%剩余空间的话,还可以跑一下
3.不要气馁,还有其他方法释放空间

方法二:secondary节点同步(推荐指数5颗星)

主要思想就是:在有新机器(磁盘)的情况下,新建一个secondary节点,使之与primary节点开始数据同步。数据的同步与直接复制数据文件不同,MongoDB会只同步数据,因此同步完成后的数据文件是没有空集合的,以此实现了磁盘空间的回收。

 

上步骤:

注意点:

注意点:
    1.一定要做好数据的备份。
    2.一定要注意主从配置
优点:
    1.同步的方法是比较好的,第一基本不会阻塞副本集的读写,
    2.第二消耗的时间相对比较短,2T数据大概在74H内同步完成,且不影响正常使用。
    3.second会清理掉大量的磁盘空间,达到我们的目的
    4.索引会自动创建,不用再手动去创建
缺点:
    1.不用于复制包含分片集合的数据库

方法三:copyDatabase(推荐指数3颗星)

MongoDB还支持在线复制数据:db.copyDatabase("from","to","IP:port"),此种方法也能释放空间,因为db.copyDatabase复制的数据,而不是表示在磁盘中的数据文件。但是该方法在4.0版本起被弃用,3.x版本还能继续使用,还可以复制远端数据库哦,方便多节点复制操作,但是需要我们备份好主库索引,手动创建索引。

主流程:

0.停掉所有业务数据读写操作
1.db.copyDatabase("from","to","127.0.0.1:16161");复制出一个新的to数据库。这个已经
是最小数据占用的数据。会在数据目录下产生to的相关数据文件。
2.将所有程序的配置从from库改为to库。测试无误。
3.这时可以删除from库。方法。use from 后 db.dropDatabase()。这个方法的好处是可以时间
将磁盘上的数据删除掉,节省出很大的空间,但是需要手动创建索引。

上方法:

前提停掉所有服务
0.Primary 对要迁移的db进行授权
use 业务DB;
db.createUser( { user: "rcrai", pwd: "xxxx", roles: [ "readWrite", "dbAdmin" ] } )
db.grantRolesToUser('rcrai',[{ role: "dbAdmin", db: "业务DB" }]) 

1.second机器上执行copy操作
db.copyDatabase("源业务DB", "目标业务DB", "10.10.10.26:30017","rcrai","xxxx");
2.等待执行完成,执行创建索引

注意点:

注意:second copy没执行完成和second索引没有手动创建完成前,一定不要启动业务读写,copy操作比较消耗IO,容易干挂docker的网络环境
优点:
    1.纯copy数据方式,速度快 2T数据耗时15小时完成
    2.支持远程复制
    3.second会清理掉大量的磁盘空间
缺点:
    1.不能自动创建索引
    2.MongoDB4.0不支持,推荐方法三
    3.不用于复制包含分片集合的数据库

方法四:mongodump And mongorestore(推荐指数2颗星)

原理简单粗暴,停服务,dump出来数据,再restore回去。

上方法:

1.停业务服务,停止业务数据读写
2.执行dump数据操作
mongodump --host 10.10.12.25 --port 30017 -u rcrai -p eawmzaxtfnptvtpxefkd --authenticationDatabase admin --oplog -o /data/dumpmongo/
3.执行restore恢复
mongorestore --host 10.10.12.26 --port 30018 -u rcrai -p eawmzaxtfnptvtpxefkd --authenticationDatabase admin --oplogReplay /data/dumpmongo

注意点:

优点:
    1.操作简单,步骤少
    2.只适用于小量数据(小于200G的场景)
缺点:
    1.如果数据量比较大(大于200G),mongorestore执行时间较长(3T数据恢复大于100H)

方法五:db.repairDatabase()(推荐指数1颗星)

官网该命令的定义:清理无效或损坏的数据并重建数据库索引。类似于文件系统修复命令fsck,所以此命令主要是用于修复数据。但是需要停机业务服务,即便你不停业务服务读写的话 MongoDB 自己也会锁住直到 repair 完成。注意要有足够的磁盘空间,需要额外一倍的空间,如果MongoDB 占用了500G,那么 repair 时还需要额外的500G+2G 空间。

上方法:

use 业务DB;
db.repairDatabase();

注意点:

 

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

优点:
    1.可以追加磁盘,然后将目标目录指向新加的磁盘
缺点:
    1.非常消耗时间
    2.在生产上操作如果意外停止可能会造成数据无法恢复的危险。
    3.如果磁盘空间不足,小于现在这个db占用的空间,这种情况是用不了

总结

最终我们采用的是第二种和第三种方法去做的磁盘清理方案,操作客户的数据最终还是要从时效性,稳定性,以及失败的可恢复性去考虑。大家一定要注意:1.客户数据基本都在T级别以上,操作大规模数据属于高危操作,每一步都要慎重,且每一个环节和步骤都要在测试环节做大数据量的测试操作。2.一定要有至少一份的备份数据,且一段时间(一周)内不能被清除。3.方案不止要准备一种,私服环境多样,根据实际情况选择合适的方案。