Carpe Diem

備忘録。https://github.com/jun06t

MongoDBのインデックス2

概要

MongoDBのインデックスにはIndex IntersectionCovered Indexなんてのもあるので調べてみました。

環境

Index Intersectionとは

1つのクエリーで2つのインデックスを使ってくれる機能で、より効率的にクエリーを処理できる
というもの。

簡単に言うと複合インデックスが必要なクエリをMongoDB側でうまくインデックスを組み合わあわせてよろしくやってくれる機能といった感じでしょうか。

基本的な使い方

db.collection.find({a: xxx, b: yyy})

といったクエリを投げるとき、最適なインデックスは

ensureIndex({a: 1, b: 1})

ですが、Index Intersectionがあると

ensureIndex({a: 1})
ensureIndex({b: 1})

という2つのインデックスをつけていればMongoDB側でうまく組み合わせてくれるという機能です。

じゃあ複合インデックスは要らない?

そんなことはないです。以下に複合インデックスとIndex Intersectionの比較を記します。

複合インデックス Index Intersection
速い。インデックスをフルに活用。
ちゃんとつければ外部ソートも起きにくい。
柔軟性。クエリ専用のインデックスを
つけていなくても効果を発揮

なので用途に応じて使い分けるといいです。
参照系のコレクションなら複合インデックスの方が向いてるでしょうし、更新系のコレクションならIndex Intersectionの方が負荷が低いと思います。

ソートの場合、完全別のインデックスは外部ソートになる

ensureIndex({a: 1})
ensureIndex({b: 1})

とした時に

find({a: xxx}).sort({b: 1})

としても外部ソートであまり効果はありません。

ensureIndex({a: 1})
ensureIndex({b: 1, c: 1})

のときに

find({a: xxx, b: yyy}).sort({c: 1})

のようにまたがる場合はだったら有効です。

prefixesは有効

ensureIndex({a: 1})
ensureIndex({b: 1, c: 1})

のときは

find({a: xxx, b: yyy})

でも

find({a: xxx, b: yyy, c: zzz})

でも有効です。

その他注意

以下のケースではIndex Intersectionは使えません。

  • rangeクエリを含む

Covered Queryとは

インデックスにデータを保持させることで、実データを取得せずインデックスだけですぐに結果を返す仕組みです。

基本的な使い方

以下の様なドキュメントを持つコレクションtestsがあるとします。

{
  _id: ObjectId(),
  a: 10,
  b: "hogehoge",
  c: "fugafuga",
  d: 100,
}

このドキュメントでほしい情報はabだけといったときに、Covered Queryが活躍します。
インデックスを以下のようにつけ、

> db.tests.ensureIndex({a: 1, b: 1})

クエリを以下のようにすれば、実データにアクセスしないためメモリ上で処理してくれます。

> db.tests.find({a: xxx}, {a: 1, b: 1, _id: 0})

注意なのは_id: 0の部分です。_idは基本的に勝手についてくるので明示的に消す必要があります。
なぜなら_idは複合インデックスに入っていないので、非表示にしないと実データにアクセスしてしまうからです。

その他注意

以下のケースではCovered Queryは使えません。

  • インデックスをつけたフィールドがarray
  • インデックスをつけたフィールドがサブドキュメント(ネストしたやつ)
  • シャードで冗長構成になっている場合

ソース