mongoDBのcount正しくない問題

mongoDBでは、シャーディングされたコレクションのcountが正しい値を返さないことがあります。これには2つの原因があります。

  1. チャンク移動中である
  2. 孤児ドキュメントが存在する

1は、チャンク移動中は正しい値が帰らないということです。シャード間でデータが移動している最中なので正しい値が取れないのは直感的に理解できると思います。

2は、孤児ドキュメント(Orphaned Documents)がカウントされてしまう問題です。孤児ドキュメントとはチャンク移動したドキュメントが元のシャードから削除されず残ってしまったものです。シャードキーの範囲外なのでmongosからは到達不能になっていますが、countには計上されてしまいます。

孤児ドキュメントを解消するには、以下のコマンドを各シャードのreplica set primaryのmongod(mongosではありません)で実行します。これは重い処理なのでサービス稼働中に実行してはいけません。

use admin;
db.runCommand({ creanupOrphaned: "<database>.<collection>" });

集計関数を使うと、孤児ドキュメントを無視して正しい件数を得ることができます。ただし、これはかなり時間がかかるので注意してください。

db.collection.aggregate(
   [
      { $group: { _id: null, count: { $sum: 1 } } }
   ]
)

db.collection.count() — MongoDB Manual 3.4

我々の環境では、2系のmongoDBでdumpし、3系の新しいクラスタへrestoreした時に孤児ドキュメントが大量に発生しました。この孤児の発生は、dumpしたデータをrestoreし、データのチャンク移動が完了したのちにremove、再度restoreすることで回避できました。初回restore時には大量のチャンク移動が発生するため、孤児ドキュメントが発生しやすいものと思われます。