41

mongodb文档说

为了压缩这个空间,从 mongo shell 运行 db.repairDatabase() (注意这个操作会阻塞并且很慢)。

http://www.mongodb.org/display/DOCS/Excessive+Disk+Space

我想知道如何让 mongodb自动释放已删除的磁盘空间?

ps 我们在mongodb中存储了很多下载任务,最大20GB,半个小时就完成了。

4

4 回答 4

68

一般来说,如果您不需要缩小数据文件,则根本不应该缩小它们。这是因为在磁盘上“增长”数据文件是一项相当昂贵的操作,MongoDB 可以在数据文件中分配的空间越多,碎片就越少。

因此,您应该尝试为数据库提供尽可能多的磁盘空间。

但是,如果您必须缩小数据库,您应该记住两件事。

  1. MongoDB 通过加倍来增长它的数据文件,因此数据文件可能是 64MB,然后是 128MB,等等,直到 2GB(此时它停止加倍以将文件保留到 2GB。)

  2. 与大多数数据库一样......要执行收缩等操作,您需要安排单独的作业来执行此操作,MongoDB 中没有“自动收缩”。事实上,主要的 noSQL 数据库(讨厌这个名字)只有 Riak 会自动收缩。因此,您需要使用操作系统的调度程序创建一个作业来运行收缩。您可以使用 bash 脚本,或者让作业运行 php 脚本等。

服务器端 Javascript

您可以使用服务器端 Javascript 通过作业(如 cron 或 windows 调度服务)定期通过 mongo 的 shell 进行收缩和运行该 JS ...

假设一个名为foo的集合,您会将下面的 javascript 保存到一个名为bar.js的文件中并运行...

$ mongo foo bar.js

javascript文件看起来像......

// Get a the current collection size.
var storage = db.foo.storageSize();
var total = db.foo.totalSize();

print('Storage Size: ' + tojson(storage));

print('TotalSize: ' + tojson(total));

print('-----------------------');
print('Running db.repairDatabase()');
print('-----------------------');

// Run repair
db.repairDatabase()

// Get new collection sizes.
var storage_a = db.foo.storageSize();
var total_a = db.foo.totalSize();

print('Storage Size: ' + tojson(storage_a));
print('TotalSize: ' + tojson(total_a));

这将运行并返回类似...

MongoDB shell version: 1.6.4
connecting to: foo
Storage Size: 51351
TotalSize: 79152
-----------------------
Running db.repairDatabase()
-----------------------
Storage Size: 40960
TotalSize: 65153

按计划运行(在非高峰时段),您就可以开始了。

封顶集合

但是,还有另一种选择,上限集合

Capped collections 是固定大小的集合,具有非常高性能的自动 FIFO 老化功能(老化基于插入顺序)。如果您熟悉的话,它们有点像“RRD”概念。

此外,自动封顶集合,高性能,维护集合中对象的插入顺序;这对于某些用例(例如日志记录)非常强大。

基本上,您可以将集合的大小(或文档数量)限制为 .. 20GB,一旦达到该限制,MongoDB 将开始丢弃最旧的记录,并在它们进入时用新条目替换它们。

这是保留大量数据的好方法,随着时间的推移丢弃旧数据并保持相同数量的磁盘空间使用。

于 2010-12-30T04:52:18.447 回答
26

如果您负担不起系统被锁定的代价,或者没有双倍的存储空间,我还有另一个解决方案可能比执行 db.repairDatabase() 更好。

您必须使用副本集。

我的想法是,一旦你删除了所有占用磁盘的多余数据,停止次要副本,擦除其数据目录,启动它并让它与主副本重新同步。

这个过程很耗时,但是当您执行 rs.stepDown() 时,它应该只花费几秒钟的停机时间。

这也不能自动化。好吧,它可以,但我不认为我愿意尝试。

于 2012-11-13T21:49:47.923 回答
8

运行 db.repairDatabase() 将要求您有与文件系统上可用数据库的当前大小相等的空间。当您知道剩下的集合或需要保留在数据库中的数据当前使用的空间比分配的空间少得多并且您没有足够的空间进行修复时,这可能会很麻烦。

作为替代方案,如果您实际上需要保留的集合很少或只需要数据的子集,则可以将需要保留的数据移动到新数据库中并删除旧数据库。如果您需要相同的数据库名称,则可以将它们移回具有相同名称的新数据库中。只要确保您重新创建任何索引。

use cleanup_database
db.dropDatabase();

use oversize_database

db.collection.find({},{}).forEach(function(doc){
    db = db.getSiblingDB("cleanup_database");
    db.collection_subset.insert(doc);
});

use oversize_database
db.dropDatabase();

use cleanup_database

db.collection_subset.find({},{}).forEach(function(doc){
    db = db.getSiblingDB("oversize_database");
    db.collection.insert(doc);
});

use oversize_database

<add indexes>
db.collection.ensureIndex({field:1});

use cleanup_database
db.dropDatabase();

具有许多集合的数据库的导出/删除/导入操作可能会达到相同的结果,但我尚未测试。

此外,作为一项策略,您可以将永久集合保存在与临时/处理数据不同的数据库中,并在作业完成后简单地删除处理数据库。由于 MongoDB 是无模式的,因此除了索引之外什么都不会丢失,并且当进程的插入下一次运行时,您的数据库和集合将被重新创建。只要确保您的工作包括在适当的时间创建任何必要的索引。

于 2013-01-14T16:56:53.670 回答
4

如果您使用的是最初编写此问题时不可用的副本集,那么您可以设置一个流程来自动回收空间,而不会导致严重的中断或性能问题。

为此,您可以利用副本集中辅助节点的自动初始同步功能。解释一下:如果您关闭辅助节点,擦除其数据文件并重新启动它,辅助节点将从集合中的其他节点之一从头开始重新同步(默认情况下,它会通过查看 ping 响应来选择离它最近的节点次)。发生此重新同步时,所有数据都从头开始重写(包括索引),有效地执行与修复相同的操作,并回收它的磁盘空间。

通过在辅助服务器上运行此程序(然后退出主服务器并重复该过程),您可以有效地回收整个设备上的磁盘空间,并且将中断降至最低。如果您正在从辅助节点读取数据,则需要小心,因为这将使辅助节点在很长一段时间内无法轮换。您还想确保您的oplog窗口足以进行成功的重新同步,但这通常是您要确定是否这样做的事情。

要自动执行此过程,您只需运行一个脚本以在不同的日子(或类似的日子)为您的集合中的每个成员执行此操作,最好是在您的安静时间或维护窗口期间。该脚本的一个非常简单的版本如下所示bash

注意:这基本上是伪代码 - 仅用于说明目的 - 请勿在没有重大更改的情况下用于生产系统

#!/bin/bash 

# First arg is host MongoDB is running on, second arg is the MongoDB port

MONGO=/path/to/mongo
MONGOHOST=$1
MONGOPORT=$2
DBPATH = /path/to/dbpath

# make sure the node we are connecting to is not the primary
while (`$MONGO --quiet --host $MONGOHOST --port $MONGOPORT --eval 'db.isMaster().ismaster'`)
do
    `$MONGO --quiet --host $MONGOHOST --port $MONGOPORT --eval 'rs.stepDown()'`
    sleep 2
done    
echo "Node is no longer primary!\n"

# Now shut down that server 
# something like (assuming user is set up for key based auth and has password-less sudo access a la ec2-user in EC2)
ssh -t user@$MONGOHOST sudo service mongodb stop

# Wipe the data files for that server

ssh -t user@$MONGOHOST sudo rm -rf $DBPATH
ssh -t user@$MONGOHOST sudo mkdir $DBPATH
ssh -t user@$MONGOHOST sudo chown mongodb:mongodb $DBPATH

# Start up server again
# similar to shutdown something like 
ssh -t user@$MONGOHOST sudo service mongodb start 
于 2014-10-23T15:21:57.063 回答