MongoDB
メンテナンス

MongoDBのCollectionを削除しても容量が減らない対応

More than 1 year has passed since last update.

MongoDBはデータを削除しても容量は解放されない。再利用されるのかどうかよくワカラナイ (検証してないので教えてくれたら嬉しい)

節約型でMongoDBを運用していたりすると「ハードディスクやばいよー」ってなる。

何度か実際に対応して問題なさそうだったのでまとめた。

環境

  • ReplicaSetで3台構成で運用
  • MongoDB Versionは 2.4.4, 2.4.8 でやった
  • OS debian squeeze

一般的な解決策

  • サービスをメンテナンスにして大きなサーバに置き換える
  • 必要ないデータを削除後、サービスをメンテナンスにして repairDatabase する

考えてみる。

大きなサーバに置き換え

メリット

  • シンプル
  • 安全そう
  • 簡単そう

デメリット

  • サービス一時停止
  • コストアップ

repairDatabase

メリット

  • コストキープ
  • 他のデータ不整合とかも取れるぽい
  • MongoDBぽい

デメリット

  • サービス一時停止
  • 時間がどのくらいかかるのか検証しないといけない
  • ブラックボックス
  • 空き容量がDB容量+2GB無いと実行できない

repairDatabase requires free disk space equal to the size of your current data set plus 2 gigabytes.
repairDatabase — MongoDB Manual 2.6.4

やりたくない

  • そもそもサービス停止は出来る限りやりたくない。
    • 少しなら仕方ないけど1日とか半日とか死亡フラグ
  • よくわからない事は取り返しつかなくなりそうなのでやりたくない
  • 当たり前だけどコストはかけないに越したことはない
  • マイノリティ根性(もっといい方法は絶対あると信じてた)

違う方法でやってみた

  1. いらないCollectionを消す
    • これは処理めっちゃ軽いのでバンバン消した
  2. しかしやはり容量は解放されず
  3. SECONDARYをからっぽにして再度syncさせたらどうなるんだっけ
  4. 容量減ったΣ(゚д゚
  5. SECONDARY1台ずつ容量へらしてsyncさせる
  6. 全部sync終わったらPRIMARYと入れ替える (短時間だがアプリ一時停止必要)
  7. 元PRIMARYもからっぽにして再sync
  8. あら不思議。メンテナンスは30分ぐらいで完了

作業内容詳細

Replicaset構成

PRIMARY
10.120.41.197
SECONDARY
10.120.41.196
SECONDARY
10.120.41.199

1.いらないCollectionを消す

set:PRIMARY> use dbname;
set:PRIMARY> db.collectionname.drop()

一瞬で消えるはず

2.SECONDARYをReplicaSetからはずす

set:PRIMARY> rs.remove("10.120.41.196:27017")

外したSECONDARYのログが落ち着くまで見守る

3.外したSECONDARYのMongoDBを止める

set:REMOVED> use admin;
set:REMOVED> db.shutdownServer();

4.外したSECONDARYのdbpathとlogpathを削除

不安な場合は圧縮してどこかにバックアップとっておけばそのまま戻せるはず (未確認)

$ sudo rm -r /var/lib/mongodb/*
$ sudo rm -r /var/log/mongodb/*

5.mongoDB復活

各OSのやり方でどうぞ

$ sudo service mongod start

6.ReplicaSet追加

追加する時にどのmemberからsyncしてくるのか設定しないと、PRIMARYからsyncしちゃってサービスに影響でちゃいます。

rs.syncFromは迅速に叩くとそこからsyncになるので毎回急いでる。 (いい方法ご存知であれば教えてほしい)

  • PRIMARY側作業
set:PRIMARY> rs.add("10.120.41.196:27017") # 追加するSECONDARY
  • 追加するSECONDARY側作業

これをPRIMARYでaddされるまで連打してる。OKのログが出ればとりあえず大丈夫
大きいDBであればsyncFromのサーバのログにslow queryとかが出るのでそこからsync出来ている事が明確になる。
もしPRIMARYでログが出てしまった場合には慌てずにrs.removeしよう

> rs.syncFrom('10.120.41.199:27017') # もう一つのSECONDARY

7.あとはsyncをまつ。

set:PRIMARY> rs.status()

を実行して追加したSECONDARYが "stateStr" : "SECONDARY", となってれば完了。

8.もう1個のSECONDARYも同じ事やる

そうするとPRIMARY以外は最適化された容量になる。

最後はPRIMARY

9.PRIMARYとSECONDARYを入れ替える

これは数秒程度書き込みが出来ないタイミングが出来てしまうので、
メンテナンスにしないとダメ。

とはいえ切り替わるのは数秒なので、経験上メンテナンス時間は30分以上かかったことはない。

  • PRIMARY側作業
set:PRIMARY> rs.freeze(15)
set:PRIMARY> rs.stepDown(1)

10.あとは元PRIMARYもからっぽにしたら完了

( ´ー`)フゥー...

これよりいい方法をご存知でしたら是非教えていただけたら嬉しいです!