mongodb磁盘满
cat /var/log/mongodb/mongodb.log
关键的错误日志:
ERROR: Insufficient free space for journal files
Please make at least 3379MB available in /var/lib/mongodb/journal or use --smallfiles
exception in initAndListen: 15926 Insufficient free space for journals, terminating
查了一下 ERROR: Insufficient free space for journal files 原因是因为 mongo 的 journa 目录下空间小于3379MB。磁盘至少要保证journal目录下有3379MB空间可用。
journal 至少以2G的数量进行增长,当磁盘空间不足时,就会报该错误。
看了一下磁盘占用满了:
df -h
/dev/sda1 91G 83G 2.8G 97% /
首先按照错误日志中解决的办法是使用 --smallfiles参数,尝试启动数据库服务。
--smallfiles 选项会减少数据文件的初始大小,并将最大大小限制为512MB。 smallfiles也会将每个日志文件的大小从1GB减少到128MB。
如果你有大量的数据库,每个数据库都包含少量的数据,那么建议使用--smallfiles 选项。
smallfiles选项可以引导mongod实例创建大量文件,这会影响较大数据库的性能,实际使用时慎重选择。
如果 journal 目录下日志文件占用空间比较大,可以关闭journal功能、删除journal目录下的文件:
vim /etc/mongodb.conf
nojournal = true
smallfiles = true
noprealloc = true
rm -rf /var/lib/mongodb/journal/*
重启数据库
service mongodb start
我journal本来就占用不大,主要还是数据库占用磁盘太大。因此我直接先采用日志推荐的--smallfiles启动试试:
在mongodb.service启动参数中加 --smallfiles 参数
vim /lib/systemd/system/mongodb.service
ExecStart=/usr/bin/mongod --unixSocketPrefix=${SOCKETPATH} --config ${CONF} $DAEMON_OPTS --smallfiles
修改服务配置文件后,重新加载一下服务配置文件
systemctl daemon-reload
重新启动mongodb服务,启动成功
service mongodb start
重启数据库服务成功,但df查看磁盘占用仍然还是很高,继续尝试解决根本的磁盘占用高的问题。
查看mongodb数据库大小的几种方法:
方法1:show databases,可查看不同数据库的大小
# mongo
> show databases;
admin 0.078GB
db01 63.923GB
local 0.078GB
mytestdb 16.071GB
test 0.578GB
可以看到主要db01和mytestdb数据库比较大。
查看指定数据局的详情:
> use mytestdb
> db.stats()
{
"db" : "mytestdb",
"collections" : 34,
"objects" : 8147600,
"avgObjSize" : 261.48154941332416,
"dataSize" : 2130447072,
"storageSize" : 13551329264,
"numExtents" : 138,
"indexes" : 32,
"indexSize" : 286724144,
"fileSize" : 17239638016,
"nsSizeMB" : 16,
"dataFileVersion" : {
"major" : 4,
"minor" : 5
},
"extentFreeList" : {
"num" : 3,
"totalSize" : 761118720
},
"ok" : 1
}
方法2:db.serverStatus().mem
# mongo
> db.serverStatus().mem
{
"bits" : 64,
"resident" : 115,
"virtual" : 165558,
"supported" : true,
"mapped" : 82666,
"mappedWithJournal" : 165332
}
mapped:映射到内存的数据大小,大小等同于数据库文件的大小
visze:占用的虚拟内存大小
res:占用的物理内存大小
方法3:mongostat
# mongostat
connected to: 127.0.0.1
insert query update delete getmore command flushes mapped vsize res faults locked db idx miss % qr|qw ar|aw netIn netOut conn time
*0 *0 *0 *0 0 1|0 0 80.7g 162g 111m 0 test:0.0% 0 0|0 0|0 62b 3k 1 20:56:29
*0 *0 *0 *0 0 1|0 0 80.7g 162g 111m 0 test:0.0% 0 0|0 0|0 62b 3k 1 20:56:30
*0 *0 *0 *0 0 1|0 0 80.7g 162g 111m 0 test:0.0% 0 0|0 0|0 62b 3k 1 20:56:31
方法4:直接查看数据库文件路径目录文件的大小
# du -sh /var/lib/mongodb/
81G /var/lib/mongodb/
主要是数据库文件较大
接下来主要考虑如何回收磁盘空间。
注意:mognodb删除数据后不会释放占用的磁盘空间给操作系统,即使 drop collection 也不行,除非drop database。
删除数据以后,dbshell 中用命令 db.serverStatus().mem 可以看到磁盘空间占用显示虽然已经释放,但df命令查看OS的空闲磁盘空间没变化,mongodb并没有释放给OS。
准备工作:
方案中可能涉及到有足够的磁盘空间来操作,可以考虑给虚机增加新的磁盘,如果没有条件,也可以考虑ssh mount远程主机磁盘到本地。
我使用ssh mount方式,sshfs安装和远程mount的详细操作方法可以参考:https://blog.csdn.net/sunny05296/article/details/77722081
安装sshfs
apt-get install -y sshfs # for ubuntu, 查看是否已安装:dpkg -l |grep wget
yum install -y fuse-sshfs # for fedore
yum install -y epel-release & yum -y install fuse-sshfs # for Centos
挂载远程网络硬盘
mkdir /remote_dir
sshfs -o rw [email protected]:/data/ remote_dir/
如果要卸载移除网盘使用:
fusermount -u /remote_dir
方案1:备份恢复(mongodump & mongorestore)【我采用了该方式】
备份
# mkdir -p /remote_dir/mongo_dump_dir
# mongodump -d mytestdb -o /remote_dir/mongo_dump_dir
备份后,目录下会生成数据库文件夹mytestdb,查看一下备份后的实际文件夹大小:
# cd /remote_dir/mongo_dump_dir
# du -h
1.6G ./mytestdb
源库16G备份删除碎片数据以后,空间占用缩小到1.6G,可见源库中存在大量的删除数据
删除源库
# echo 'db.dropDatabase()' | mongo mytestdb
恢复源库
# mongorestore -d mytestdb /remote_dir/mongo_dump_dir/mytestdb
用同样的方法,我处理完 db01 库会后,大小从 61G 减少到 19G。
如果数据量不大、dump时间不长,或者经常备份有dump文件的情况的下,这种方法很简单方便。
方案2:db.repairDatabase() 【我剩余空间太少,没有尝试该方式】
官方介绍说用:删除数据库的数据以后,需要 dbshell 进入删除了数据的 db 后,执行 db.repairDatabase() 可以回收硬盘空间。
但是要注意风险:
1.在生产上操作如果意外停止可能会造成数据无法恢复的危险。
2.如果磁盘空间不足,小于现在这个db时间占有的空间,这种情况是用不了 db.repairDatabase() 的。
需要注意,db.repairDatabase() 能否执行成功,主要处决于:释放出来的磁盘空间小于释放执行前当前OS剩余的磁盘空间,则能释放成功,否则释放失败。
而我声誉磁盘空间只有2.8G,空闲磁盘太少了, 删除数据再db.repairDatabase()的话,估计失败的概率很大。所以,我直接放弃下面的命令执行:
# mongo db01
mongo> db.repairDatabase()
或者执行
mongo> db.runCommand({ repairDatabase: 1 })
后面这种方法还支持带更多的参数,这里就先不详细介绍了。
方案3:db.copyDatabase 【由于我只有一个mongodb,所以没有采用该方式】
mongo> db.copyDatabase("db_src","db_src_copyed","127.0.0.1:27017")
如果是同一个mongodb拷贝,可以直接用:db.copyDatabase("db_src","db_src_copyed")
mongo> use db_src
mongo> db.dropDatabase()
完整的语法:db.copyDatabase(fromdb, todb, fromhost, username, password, mechanism)
fromdb:源db名,用户必须能够对这个db进行鉴权
todb: 复制到目的mongod的名字,名字可以跟原名字不一样
fromhost: ip:port 如果是同一个mongodb拷贝,这个参数可不要
username: 用户名
password: 密码
mechanism:鉴权方式,MONGODB-CR or SCRAM-SHA-1,如果 db.isMaster().maxWireVersion >= 3,那默认就是 SCRAM-SHA-1,否则默认就是 MONGODB-CR
执行数据库拷贝,源库可能是已经删除过数据的库,由于磁盘不释放,造成磁盘空间的浪费。
通过数据库拷贝命令,拷贝到目的库,不会拷贝碎片,会生成一个最小占用的库(数据库目录下也会同步生成新库的文件)。
拷贝库成功后,就可以使用 db.dropDatabase() 删除源库了。