MongoDBベストプラクティス

3032 ワード

本文は書籍とネットブログの整理と個人の実践から整理した.
1.コンポジット文書の使用
MongoDBはドキュメントデータベースであり、各レコードがJSON形式のドキュメントであることを知っています.たとえば、次の例では、毎日このような統計データが生成されます.
{ metric: "content_count", client: 5, value: 51, date: ISODate("2012-04-01 13:00") }
{ metric: "content_count", client: 5, value: 49, date: ISODate("2012-04-02 13:00") }

コンビネーション・ドキュメントを使用すると、1ヶ月のデータをすべて1つのレコードに保存できます.
{ metric: "content_count", client: 5, month: "2012-04", 1: 51, 2: 49, ... }

上記の2つの方法で記憶し、予め合計約7 GBのデータ(機械は1.7 GBのメモリしかない)を記憶し、1年間の情報の読み取りをテストし、この2つの読み取り性能の違いは明らかである.
1つ目:1.6秒2つ目:0.3秒では、問題はどこにあるのでしょうか.
実際には、データの読み取り時にコンビネーション式に格納されているため、より少ないドキュメント数を読み取ることができます.読み取りドキュメントが完全にメモリに存在しない場合、その代価は主にディスクseekに費やされ、最初の記憶方式は1年間のデータを取得する際に読み取りが必要なドキュメントの数が多くなるため、ディスクseekの数も多くなる.だからもっと遅いです.
実際、MongoDBの有名なユーザーfoursquareは、このような方法を多く採用して読み取り性能を向上させています.これを見て
2.特殊なインデックス構造を採用
MongoDBは従来のデータベースと同様に,インデックスとしてBツリーを用いたデータ構造であることが知られている.ツリー型のインデックスでは、ホットデータを保存するために使用されるインデックスがストレージに集中するほど、インデックスの無駄なメモリも小さくなります.次の2つのインデックス構造を比較します.
db.metrics.ensureIndex({ metric: 1, client: 1, date: 1})

db.metrics.ensureIndex({ date: 1, metric: 1, client: 1 })

この2つの異なる構造を採用すると,挿入性能の差も顕著である.
第1の構成を採用すると、データ量が2千万以下の場合、10 k/sの挿入速度をほぼ維持することができ、データ量がさらに大きくなると、その挿入速度は2.5 k/sに徐々に低下し、データ量がさらに大きくなると、その性能はより低くなる可能性がある.
一方、第2の構造を採用すると、挿入速度は10 k/sにほぼ安定することができる.
これは、2つ目の構造がdateフィールドをインデックスの1番目に配置しているため、インデックスを構築する際に、新しいデータがインデックスを更新する際に、中間で更新するのではなく、インデックスの末尾で修正するだけです.挿入時間が早すぎるインデックスは、後続の挿入操作でほとんど変更する必要はありません.1つ目の場合、dateフィールドが先頭にないため、インデックス更新はツリー構造の真ん中で発生することが多く、インデックス構造が大規模に変化することが多い.
3.予約スペース
第1点と同様に、従来の機械的ハードディスクの主な動作時間がディスクseek動作に費やされていることを考慮している.
例えば第1点の例では,我々はデータを挿入する際に,この年のデータに必要な空間を一度に挿入しておく.これにより、私たちのこの年12ヶ月のデータが1つの記録に順次格納されていることが保証されます.では、読み取りの際、ディスクの順番読み取り操作を1回で1年のデータを読むことができるかもしれません.前の12回の読み取りに比べて、磁気ディスクseekも1回しかありません.
db.metrics.insert([
    { metric: 'content_count', client: 3, date: '2012-01', 0: 0, 1: 0, 2: 0, ... }
    { .................................., date: '2012-02', ... })
    { .................................., date: '2012-03', ... })
    { .................................., date: '2012-04', ... })
    { .................................., date: '2012-05', ... })
    { .................................., date: '2012-06', ... })
    { .................................., date: '2012-07', ... })
    { .................................., date: '2012-08', ... })
    { .................................., date: '2012-09', ... })
    { .................................., date: '2012-10', ... })
    { .................................., date: '2012-11', ... })
    { .................................., date: '2012-12', ... })
])

結果:
予約スペースを採用しない場合、1年間の記録を読み取るには62 ms が必要となる.
予備空間方式を採用すれば、1年間の記録を読み取るには6.6 ms しかかからない.