web-dev-qa-db-ja.com

mongodbで削除されたスペースを自動圧縮しますか?

Mongodb文書には、

このスペースを圧縮するには、mongoシェルからdb.repairDatabase()を実行します(この操作はブロックされ、遅くなります)。

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

Mongodbを削除したディスクの空き容量を自動的ににする方法はありますか?

追伸多くのダウンロードタスクを最大20GBのmongodbに保存し、30分で完了しました。

39
Zealot Ke

一般に、データファイルを縮小する必要がない場合は、まったく縮小しないでください。これは、ディスク上のデータファイルの「成長」はかなり高価な操作であり、MongoDBがデータファイルに割り当てることができるスペースが多くなるほど、断片化が少なくなるためです。

そのため、データベースにできるだけ多くのディスク領域を提供するようにしてください。

ただし、データベースを縮小する必要がある場合は、2つの点に留意する必要があります。

  1. MongoDBは、データファイルを2倍にすることでデータファイルを拡大し、データファイルは64 MB、128 MBなど、最大2 GBになります(この時点で、2 GBまでファイルを保持するために倍増を停止します)。

  2. ほとんどのデータベースと同様に、縮小などの操作を行うには、個別のジョブをスケジュールする必要があります。MongoDBには「自動縮小」はありません。実際、主要なnoSQLデータベース(その名前は嫌いです)では、Riakのみが自動縮小します。したがって、縮小を実行するには、OSのスケジューラを使用してジョブを作成する必要があります。 bashスクリプトを使用したり、ジョブでphpスクリプトを実行したりできます。

サーバーサイドJavascript

サーバー側のJavascriptを使用して縮小を実行し、ジョブ(cronやWindowsスケジューリングサービスなど)を介して定期的にmongoのシェル経由で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

これをスケジュール(ピーク時以外)で実行すると、準備完了です。

キャップ付きコレクション

ただし、他に1つのオプション capped collections があります。

上限付きコレクションは、非常に高性能の自動FIFOエージアウト機能を備えた固定サイズのコレクションです(エージアウトは挿入順序に基づいています)。あなたがそれに精通しているなら、それらは「RRD」概念に少し似ています。

さらに、上限付きコレクションは、自動的に高性能でコレクション内のオブジェクトの挿入順序を維持します。これは、ロギングなどの特定のユースケースでは非常に強力です。

基本的に、コレクションのサイズ(または内のドキュメント数)を20 GBと制限できます。この制限に達すると、MongoDBは最も古いレコードを破棄し始め、新しいエントリに置き換えます。

これは、大量のデータを保持し、時間が経つにつれて古いデータを破棄し、同じ量のディスクスペースを使用するのに最適な方法です。

65
Justin Jenkins

システムをロックする余裕がない場合、またはストレージを2倍にできない場合、db.repairDatabase()を実行するよりも適切に動作する別のソリューションがあります。

レプリカセットを使用している必要があります。

私の考えは、ディスクをゴブリングしている余分なデータをすべて削除し、セカンダリレプリカを停止し、そのデータディレクトリを消去し、起動して、マスターと再同期させることです。

このプロセスには時間がかかりますが、rs.stepDown()を実行すると、数秒のダウンタイムしかかかりません。

また、これは自動化できません。まあそれはできましたが、私は試してみたいとは思いません。

26
Mojo

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はスキーマレスであるため、インデックス以外は何も失われず、プロセスの挿入が次に実行されるときにデータベースとコレクションが再作成されます。ジョブに適切なタイミングでのネセカリーインデックスの作成が含まれていることを確認してください。

8
Robert Jobson

replica sets を使用している場合は、この質問が最初に記述されたときには利用できなかったため、重大な混乱やパフォーマンスの問題を引き起こすことなく、スペースを自動的に回収するプロセスを設定できます。

そのためには、レプリカセットのセカンダリの自動初期同期機能を利用します。説明:セカンダリをシャットダウンし、そのデータファイルを消去して再起動すると、セカンダリはセット内の他のノードの1つからゼロから再同期します(デフォルトでは、ping応答を見て、最も近いノードを選択します)回)。この再同期が発生すると、すべてのデータ(インデックスを含む)が最初から書き直され、修復と実質的に同じことを実行し、ディスク領域が再利用されます。

これをセカンダリで実行する(そしてプライマリをステップダウンしてプロセスを繰り返す)ことにより、セット全体のディスク領域を最小限の中断で効果的に再生できます。セカンダリから読み込む場合は注意が必要です。これは、潜在的に長い間ローテーションからセカンダリを取得するためです。また、再同期を成功させるには oplog ウィンドウで十分であることを確認する必要がありますが、通常はこれを行うかどうかを確認する必要があります。

このプロセスを自動化するには、セットの各メンバーに対して、できれば静かな時間やメンテナンス時間中に、別の日(または同様の日)にこのアクションを実行するスクリプトを実行する必要があります。このスクリプトの非常に単純なバージョンは、bashで次のようになります。

注:このIS基本的な擬似コード-例示目的のみ-重大な変更のない本番システムには使用しないでください

#!/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 
4
Adam Comerford