Humanity

Edit the world by your favorite way

MongoDB で190万件あるコレクションに $regex で LIKE 検索する時にパフォーマンスの観点で気を付けること

控えめに150万件以上と言ったけど実際は190万件ぐらいだった。 以下の全てのクエリは 0 件を返す。 というか今見たら $and 使う必要はなかった。

// 89790ms
db.getCollection('colName').find({$and: [{name: /(あああ)/}, {status: 1}]})
// 1361ms
db.getCollection('colName').find({$and: [{name: /^(あああ)/}, {status: 1}]}).hint({name: 1})
// 4ms
db.getCollection('colName').find({$and: [{name: /^あああ/}, {status: 1}]}).hint({name: 1})
// 107386ms
db.getCollection('colName').find({$and: [{name: /^(あああ)/}, {status: 1}]})
// 86117ms
db.getCollection('colName').find({$and: [{name: /^あああ/}, {status: 1}]})

/(あああ)/ よりヒントなしの /^(あああ)/ が遅いのが謎。 ちなみに文字列検索した時は普通にインデックスが効くっぽいのですぐ返ってくる。

// 15ms
db.getCollection('colName').find({$and: [{name: "あああ"}, {status: 1}]})

公式ドキュメント

$regexIndex Use の項参照。

  • case insensitive なパターンを使うこと
  • ^\A で始めること
  • 先頭からのマッチの長さをなるべく短くする
    • 例えば /^a/, /^a.*/, /^a.*$/ だったら /^a/ が一番速い
    • [私見] 安易に .* とか書かずにどうしても必要なら non-greedy match にするといいかも (.*?)

あとドキュメントに書いてないけど冒頭の結果から分かるのは

  • () を使わない

もですかね。

TODO

$explain した結果も貼る。