mongodb磁盘满故障

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() 删除源库了。
 

你可能感兴趣的:(数据库,常见问题)