私は製品データを保存するMongoDBを持っています204.639.403アイテムについては、そのデータがすでにアイテムの国別にまとめられています。4つの論理同じ物理マシン上の同じ MongoDB プロセスで実行されているデータベース。
論理データベースごとのドキュメント数のリストは次のとおりです。
- クック:56.719.977
- 出:61.216.165
- フランス語:52.280.460
- それ:34.422.801
私の問題は、データベースの書き込みパフォーマンスが悪化していることです。特に、4つのデータベースのうち最大のもの(De)への書き込みが、iotop
mongodプロセスの使用状況によると、非常に悪くなっています。99%IO時間の3MB未満の書き込みと1.5MB/秒未満の読み取り。これにより、データベースのロックが長くなり、100%以上ロックは、他の国のデータベースへの書き込みと読み取りのすべてのプロセスが停止している場合でも、正常に実行されますmongostat
。現在のスレーブは最大 6 の負荷に達し、レプリカ セット マスターは同時に 2 ~ 3 の負荷がかかるため、レプリケーション ラグも発生します。
各データベースは同じデータとインデックス構造を持ちますが、ここでは例として最大のデータベース (De) のみを使用しています。
これは、例としてデータベースから取得されたランダムな項目です。構造は、1 回の読み取りですべての重要なデータを収集するように最適化されています。
{
"_id" : ObjectId("533b675dba0e381ecf4daa86"),
"ProductId" : "XGW1-E002F-DW",
"Title" : "Sample item",
"OfferNew" : {
"Count" : 7,
"LowestPrice" : 2631,
"OfferCondition" : "NEW"
},
"Country" : "de",
"ImageUrl" : "http://….jpg",
"OfferHistoryNew" : [
…
{
"Date" : ISODate("2014-06-01T23:22:10.940+02:00"),
"Value" : {
"Count" : 10,
"LowestPrice" : 2171,
"OfferCondition" : "NEW"
}
}
],
"Processed" : ISODate("2014-06-09T23:22:10.940+02:00"),
"Eans" : [
"9781241461959"
],
"OfferUsed" : {
"Count" : 1,
"LowestPrice" : 5660,
"OfferCondition" : "USED"
},
"Categories" : [
NumberLong(186606),
NumberLong(541686),
NumberLong(288100),
NumberLong(143),
NumberLong(15777241)
]
}
一般的なクエリは、ProductId や EAN のみによる単純なものから、カテゴリによる絞り込みや A ランクによる並べ替え、またはカテゴリと A ランクの範囲 (たとえば 1 から 10,000 まで) による絞り込みや B ランクによる並べ替えなどです。
最大のデータベースからの統計は次のとおりです:
{
"ns" : "De.Item",
"count" : 61216165,
"size" : 43915150656,
"avgObjSize" : 717,
"storageSize" : 45795192544,
"numExtents" : 42,
"nindexes" : 6,
"lastExtentSize" : 2146426864,
"paddingFactor" : 1,
"systemFlags" : 0,
"userFlags" : 1,
"totalIndexSize" : 41356824320,
"indexSizes" : {
"_id_" : 2544027808,
"RankA_1" : 1718096464,
"Categories_1_RankA_1_RankB_-1" : 16383534832,
"Eans_1" : 2846073776,
"Categories_1_RankA_-1" : 15115290064,
"ProductId_1" : 2749801376
},
"ok" : 1
}
注目すべきは、インデックスのサイズがストレージサイズのほぼ半分。
各国DB1日あたり300万~500万件の更新/挿入を処理する必要がある私の目標は、夜間に 5 時間以内に書き込み操作を実行することです。
現在は 2 台のサーバーで構成されたレプリカ セットで、各サーバーには 32GB の RAM と 2TB の HDD による RAID1 が搭載されています。デッドロック スケジューラや noatime などの単純な最適化はすでに行われています。
私はいくつかの最適化戦略を考え出しました:
- 数値インデックスの削減:
-
- デフォルトの _id では、デフォルトの MongoId の代わりに ProductId を使用できます。これにより、DB あたりの合計ニクス サイズが 6 ~ 7% 節約されます。
-
- Categories_1_RankA_-1 インデックスを削除しようとすると、BrowseNodes_1_RankA_1_RankB_-1 インデックスでもクエリを処理できる可能性があります。完全なインデックスが使用されていない場合でも、並べ替えは適切に実行されますか? 別の方法としては、メイン コレクションを参照する別のコレクションに Categories_1_RankA_1_RankB_-1 に一致するインデックスを格納することです。
- 「Categories」、「Eans」、「OfferHistoryNew」の代わりに、より小さなキーを使用して生データの量を減らします。「a」、「b」、「c」を使用できます。これは、私が使用したので簡単なはずです。http://mongojack.org/しかし、それがどれほど価値があるかはわかりません。
- RAID1 を RAID0 に置き換えることは、スレーブをダウンさせて再インストールし、レプリカ セットに読み込むことで簡単にテストできます。
- より強力なハードウェア SSD と、読み取りと書き込みをより高速に処理できるメモリをテストします。
- MongoDB のシェーディング機能を使用します。
-
- 各シャードはデータベースインデックス全体を保持する必要があると読みました。
-
- クエリ構造が共有環境にうまく適合しないかもしれないという懸念があります。製品 ID をシャード キーとして使用することは、すべてのクエリ タイプに適合するわけではないようですし、カテゴリによるシャーディングも複雑です。1 つのアイテムが複数のメイン カテゴリとサブ カテゴリにリストされることがあります...。私の懸念は間違っているかもしれません。実稼働環境では使用したことはありません。
しかし、他にも最適化戦略があるはずですが、私が聞きたいことは思いつきませんでした。
どの最適化戦略が最も有望に思えますか、それとも複数の最適化の組み合わせが必要ですか?
ベストアンサー1
おそらく記録的な成長により問題が発生しているのでしょう。http://docs.mongodb.org/manual/core/write-performance/#document-growth。
Mongo では、固定 (または少なくとも制限された) サイズのレコードが推奨されます。レコード サイズを事前に割り当てられたストレージを超えて増やすと、ドキュメントがディスク上の別の場所に移動され、書き込みごとに I/O が増加します。ドキュメントのサイズが比較的均一である場合は、挿入時に平均的なドキュメントに「十分な」スペースを事前に割り当てることを検討してください。そうでない場合は、急速に増大するネストされた配列を別のコレクションに分割して、更新を挿入に置き換えることを検討してください。また、断片化をチェックし、データベースを定期的に圧縮することを検討してください。これにより、ブロックあたりのドキュメントの密度が高くなり、ハード ページ フォールトが削減されます。