MongoDBインデックス実戦テクニック


本文の内容はKyle BankerのMongoDB In Actionの本に由来する.主にMongoDBインデックスに関するいくつかの基礎知識と使用テクニックについて説明します.
索引タイプ
MongoDBのインデックスはストレージ構造上は同じですが、アプリケーション層のニーズに応じてユニークインデックス(unique)、疎インデックス(sparse)、多値インデックス(multikey)などいくつかのタイプに分けられます.
ユニークインデックス
ユニークインデックス作成時にunique:trueのオプションを追加すれば、作成コマンドは次のとおりです.
db.users.ensureIndex({username: 1}, {unique: true})

上記の一意のインデックスが作成されると、insertのusernameにすでに存在するデータがある場合、次のエラーが表示されます.
E11000 duplicate key error index: gardening.users.$username_1 dup key: { : "kbanker" }

既存のデータのcollectionで一意のインデックスを作成し、一意のインデックスに対応するフィールドに重複するデータ・アイテムがある場合は、作成に失敗します.dropDupsのオプションを追加して、重複するアイテムを強制的に削除する必要があります.コマンドは次の例です.
db.users.ensureIndex({username: 1}, {unique: true, dropDups: true})

ばらばら索引
データの一部の行にフィールドまたはフィールド値がnullでない場合、このフィールドに通常のインデックスが作成されている場合、このフィールドまたは値nullのない行もインデックス構造に参加し、対応するスペースを占有します.これらの値が空のローをインデックスに参加させたくない場合は、分散インデックスを使用できます.分散インデックスは、指定したフィールドが空でないローだけをインデックス作成に参加させます.次のコマンドを使用して、分散インデックスを作成します.
db.reviews.ensureIndex({user_id: 1}, {sparse: true})

多値索引
MongoDBは、arrayタイプに対して次のようなインデックスを作成できます.MongoDBはtagsフィールドにインデックスを作成できます.
{ name: "Wheelbarrow",
tags: ["tools", "gardening", "soil"]
}

インデックスを生成すると、tagsの3つの値にそれぞれ3つのインデックス要素が生成され、インデックスのtools、gardening、soilの3つの値が同じ行のデータを指します.3つの独立したインデックス・アイテムに分割することに相当します.
インデックス管理
インデックスの作成と削除
インデックスの作成と削除の方法はいろいろありますが、次の2つはシステムを比較する方法です.indexesというcollectionは、インデックスの作成を完了するために適切な書き込み操作を行います.
spec = {ns: "green.users", key: {‘addresses.zip’: 1}, name: ‘zip’}
db.system.indexes.insert(spec, true)

上のコマンドはシステムに行きます.indexesには、インデックスを作成するcollectionの名前空間、インデックスの情報、およびインデックスの名前を含むレコードが書き込まれます.
作成が完了すると、次のコマンドで作成したインデックスを見つけることができます.
db.system.indexes.find()
{ "_id" : ObjectId("4d2205c4051f853d46447e95"), "ns" : "green.users",
"key" : { "addresses.zip" : 1 }, "name" : "zip", "v" : 0 }

作成したインデックスを削除するには、次のコマンドを使用します.
use green
db.runCommand({deleteIndexes: "users", index: "zip"})

索引作成コマンド
実際にインデックスを作成するには、openとcloseの2つのフィールドの結合インデックスを作成するなど、ensureIndexの方が便利です.次のコマンドを使用できます.
db.values.ensureIndex({open: 1, close: 1})

このコマンドは、インデックスを作成する2つのプロセスをトリガーします.1つは、対応するフィールドをソートすることです.インデックスはB+ツリーで構成されているため、ツリーを構築するには、データをソートすると、B+ツリーを挿入する効率(2番目のプロセスの効率)が向上します.ログには、次のような出力が表示されます.
Tue Jan 4 09:58:17 [conn1] building new index on { open: 1.0, close: 1.0 } for stocks.values
1000000/4308303 23%
2000000/4308303 46%
3000000/4308303 69%
4000000/4308303 92%
Tue Jan 4 09:59:13 [conn1] external sort used : 5 files in 55 secs

2つ目の手順は、ソートされたデータをインデックス構造に挿入し、使用可能なインデックスを構成することです.
1200300/4308303 27%
2227900/4308303 51%
2837100/4308303 65%
3278100/4308303 76%
3783300/4308303 87%
4075500/4308303 94%
Tue Jan 4 10:00:16 [conn1] done building bottom layer, going to commit
Tue Jan 4 10:00:16 [conn1] done for 4308303 records 118.942secs
Tue Jan 4 10:00:16 [conn1] insert stocks.system.indexes 118942ms

ログの出力に加えて、端末でcurrentOpコマンドを実行することで、現在の操作スレッドに関する情報を取得することもできます.次の例では、次のようにします.
> db.currentOp()
{
"inprog" : [
{
"opid" : 58,
"active" : true,
"lockType" : "write",
"waitingForLock" : false,
"secs_running" : 55,
"op" : "insert",
"ns" : "stocks.system.indexes",
"query" : {
},
"client" : "127.0.0.1:53421",
"desc" : "conn",
"msg" : "index: (1/3) external sort 3999999/4308303 92%"
}
]
}

最後の部分はインデックス構築プロセスで、現在92%までソートプロセスが実行されています.
バックグラウンドにインデックスを作成
インデックスを作成すると、データベースに書き込みロックが追加され、データセットが大きい場合は、インデックスの作成が終了するまでオンライン読み書きデータベースの操作が保留されます.これはデータベースの通常のサービスに影響します.インデックスの作成時にbackground:trueのオプションを追加することで、作成作業をバックグラウンドで実行できます.この場合、インデックスの作成には書き込みロックが必要ですが、この書き込みロックはインデックスの作成が完了するまで直接独占するのではなく、他の読み書き操作のために一時停止し、深刻なパフォーマンスの影響を及ぼすことはありません.具体的な方法:
db.values.ensureIndex({open: 1, close: 1}, {background: true})

索引のオフライン作成
いずれにしても、インデックスの作成はデータベースに一定の圧力を与え、オンラインサービスに影響を与えます.インデックスを作成するプロセスがオンラインサービスに全く影響しない場合は、replica setsのノードをクラスタから剥離し、このノードに対応するインデックスを追加し、インデックスが追加されてからreplica setsに追加できます.これは、インデックスの作成時間がoplogがログを保存できる時間より長くないことを保証する必要があります.そうしないと、作成後にノードがオンラインになってprimaryに追いつけないことがわかり、resync操作が行われます.
インデックス・バックアップ
mongodumpコマンドを使用してもmongoexportコマンドを使用しても、データのバックアップだけでインデックスのバックアップはできません.リカバリ時には、長いインデックス作成プロセスを待つ必要があります.したがって、バックアップ時にインデックスを付ける場合は、データファイルをバックアップする方法が望ましいです.
インデックス圧縮
インデックスはしばらく使用すると、削除などの操作を経て、比較的緩やかになり、不要な空間を戦い、reindexコマンドを通じてインデックスを再組織し、インデックスの空間占有量を小さくすることができます.