ひとりMongoDB University / M201 MongoDB Performance(8)
この記録は、アドベントカレンダー形式ではじめた、MongoDB Universityの学習コースの記録の続きになります!
ただいまのコース
- M201 MongoDB Performance
このコースでは、開発者 / オペレーション担当者双方向けの中級レベルの内容とのことです。
前回の記事は、ひとりMongoDB University / M201 MongoDB Performance(7) でした。
Chapter 5: Performance on Clusters
Performance Considerations in Distributed Systems Part 1 (動画)
MongoDB のクラスタには2つの目的があります。
- レプリケーションによる高可用性のため
- シャーディングによる水平方向へのスケールアウトのため
- いずれも分散環境の場合は以下を考慮
- データのやりとりのレイテンシ
- 異なるノードへのデータの分散度合い
- 読み込み、あるいは書き込みの度合いがどれだけあるか
レプリケーションの場合
- 高可用性のためのしくみ
- mongos は使いません
- それぞれのノードのmongodが稼働
- アプリケーションはプライマリのmongodに問い合わせに行く
- プライマリに何かあっても大丈夫なように、セカンダリノードも大事
- そのほかにも、セカンダリへ特定の役割をオフロードできる仕組みもある
シャーディングの場合
- 水平方向へのスケールが目的
- mongos, Config Server が必要になる
- mongos, Config Server 自体も複数立てることができる
- シャードノード自体は、レプリカセットを構成している
シャーディングを選択する前の考慮点
- 本当に垂直方向のスケールアップの制限に達している?
- スケールアップがコストと性能に見合うなら、無理にシャーディングを選択する必要はないよ!
- アプリケーションによるデータのアクセスパターン、増加のパターンも考慮すること
- シャードキーを使ってデータを分散させるため、頻繁に読み書きのアクセスがあるキーが同一ノードに偏るような場合は効果が出ないかも
- データセットのシャードキーに何を利用するかが大事
Performance Considerations in Distributed Systems Part 2 (動画)
- シャーディングの構成の場合は、アプリケーションからmongos へ問い合わせし、mongos から Config Server への問い合わせ、それを受けて実際のノードへのアクセスが走る
- 複数のノードをホップするため、ネットワークのレイテンシや帯域が非常にパフォーマンスにとって重要になる
- クエリの結果、単一のノードに対するデータの取得になるか、複数のノードにまたがってデータを取得しアプリケーションに返す形になるかでも、パフォーマンスが変わってくる
シャーディングされたデータに対してのソートやlimitについて
- まずシャードキーを基にクエリの条件を満たすデータを各ノードに取りに行く
- ソートが必要な場合は、各ノード内において取得できたデータについてソートを実施する
- 最後に、プライマリのシャードが結果を集約し、最終的にソート結果をマージしたものがmongosに返される
- limit の場合も、各ノードにおいてローカルにlimitに応じた値を抽出する
- 同じくプライマリシャードが結果をマージして、最終的にlimitされた値を返す
Check: Performance Considerations in Distributed Systems Part 2
分散データーベースを利用する場合、パフォーマンスの観点から考慮すべきことは....
- クエリがどのようにルーティングされるか
- レイテンシを抑えられるか
Increasing Write Performance with Sharding Part 1 (動画)
-
垂直スケーリングと水平スケーリング
-
垂直スケーリングの場合は、コストとパフォーマンスがリニアに比例するわけではない
-
水平スケーリングは、個々のノードはそこまで性能が求められない
- ノードも同じハードウェアではなくても良い
-
シャードされたクラスタで考慮すべきは、クライアントとのやりとりは mongos を通すこと
- mongos がどのクラスタノードにデータを読み書きすべきかを取りまとめているため
-
シャードキーによりデータがどのノードに配置されるかが決まる
-
シャードキーはドキュメントに必ず存在するフィールドを対象とすること
-
シャードキーにより、データは「チャンク」と呼ばれるサイズ単位に分割される
- このチャンクがクラスタノードに均一に分散させるようにすることが重要
3つのポイント
- カーディナリティ(データの種類、値のばらつき度合い)
- フリークエンシー
- レート
カーディナリティ
- データの種類、ばらつきの度合い
- ばらつき度合いが少ない(低い)と、少ないチャンクにデータが密集する形になる
- データの種類が1つしかないフィールドをシャードキーにした場合
- 結果的に、必要とするシャーディング用のノードは1つだけになってしまう
- こういった場合は、ほかにカーディナリティが高いフィールドと組み合わせ、複合シャードキーとする方法がある - 例:住んでいる州と、名前の組み合わせなど
- 結果的に、必要とするシャーディング用のノードは1つだけになってしまう
Increasing Write Performance with Sharding Part 2 (動画)
フリークエンシーについて (相対頻度)
- データの出現頻度によってもシャードに偏りが出てくる
- last_name をシャードキーにした場合、last_name: Brown に該当するデータが多いと、チャンクのサイズが大きくなる
- そのデータを持つノードがホットシャードとなってしまい、アクセスが集中する結果になる
- こういったデータの性質を持つシャードキーは、複合シャードキーとすると良い- たとえば、last_name と _id を組み合わせるなど
レートについて
- シャードキーに該当する値がユニークであっても、増え方が単一方向である場合は、範囲指定のクエリなどの場合そのシャードにアクセスが集中してしまう
- たとえば _id をシャードキーにしたとして、単純にインクリメントするタイプの _id であるような場合が該当
- このようなケースでも、複合シャードキーを検討すると良い
bulk write について
- bulk write のオプションで、orderf: true を指定した場合、シャーディング環境に対して書き込む時に、各シャード単位でまとめて書き込みし、終わったら次のシャードに移るという動作になる
- シャーディングされたクラスタは、シャード単位でレプリケーションも行っているため、シャードのノードとレプリカセットの書き込みまでを1単位とすると、処理が遅くなる
- ordered: false の場合は、mongos が並列にシャードに書き込みに行く
Check: Increasing Write Performance with Sharding Part 2
シャーディングの環境の場合に考慮すること:
- Picking a good shard key is one of the most important parts of sharding.
- 適切なシャードキーを選ぶことが重要
Reading from Secondaries (動画)
- レプリケーション構成時、セカンダリノードから明示的に読み取りを指定するのはどのようなケースが適しているか
- 読み取りのデフォルトはプライマリだが、オプションで切替が可能
- プライマリ優先 (primaryPreferred)
- セカンダリ (secondary)
- セカンダリがメインでプライマリも許容 (secondaryPreferred)
- 地理的に近いところ (nearest)
Ref. https://docs.mongodb.com/manual/core/read-preference/
# readPref() は、mongosh でのメソッドです!
# node.js では指定が異なります
# Ref. https://docs.mongodb.com/manual/reference/method/cursor.readPref/
db.collection.find({ }).readPref( "primary")
db.collection.find({ }).readPref( "primaryPreferred")
db.collection.find({ }).readPref( "secondary")
db.collection.find({ }).readPref( "secondaryPreferred")
db.collection.find({ }).readPref( "nearrest")
- readPref() で指定した場合でも、書き込みはプライマリ
- レプリケーションの場合は、プライマリに書き込まれるのと同等のデータが書き込まれる
- 小さいサーバが割り当たっていると意外と読み書き負担が高くなる
- secondaryPreferred の場合は、セカンダリノードのいずれか
- セカンダリが無い場合はプライマリを参照
- nearrest の場合は、各ノードとのハートビートメッセージから、最も近いノードを判断
- セカンダリノードからの読み取りでは、まだデータが反映されていない stale なものを読み取る場合がある
- 追加されていない、だけでなく変更が反映されていなかったり、プライマリでの削除があってもまだ削除が完了していない、など
- stale なデータの読み取りを許容するアプリケーションやサービスでの指定が適している
- 地理的な位置を考慮して分散しているケース(ローカルリード)の場合は適している
Check: Reading from Secondaries
どんな時にセカンダリ(プライマリ)でないノードから読み取るのが良いか。
- To provide reads with lower latency. (低いレイテンシでデータの読み取りが求められる場合にむいている)
- When doing ad-hoc queries and analytic jobs. (長時間かかる分析系のデータ処理に向いている)
Replica Sets with Differing Indexes Part 1 (動画)
セカンダリノードにだけ、特定のインデックスを設定するケース。
通常の構成ではこの設定は想定していない。
- 分析用にセカンダリノードからデータを抽出するケース
- 遅延が見込まれるノードを避けてデータを抽出するケース
- テキストサーチの場合
この構成をする場合は、対象のセカンダリノードにも条件がある。
- Priority = 0 / プライマリに昇格させないノードである
- Hidden node である
- あるいは、遅延の発生するセカンダリノードである
- セカンダリノードであっても、反映に遅れのあるノードである
Replica Sets with Differing Indexes Part 2 (動画)
セカンダリノードに対してだけインデックスを設定する例。
- 対象とするセカンダリノードの Priority を0にして起動
- mongosh で、セカンダリのアドレスを明示して接続
- セカンダリノードに接続した状態で createIndex を実施
Replica Sets with Differing Indexes Part 3 (動画)
- 特定のセカンダリノードにだけ作成したインデックスを使ってクエリ実行(デモ)
- パターンマッチでの文字列検索など、時間がかかるものを指定
- この構成は一般的でないので、分析や遅延対策といった特定のケースの場合に利用すること
Check: Replica Sets with Differing Indexes Part 3
どういう条件で、セカンダリノード限定のインデックスが指定可能か。
- A secondary should never be allowed to become primary
Aggregation Pipeline on a Sharded Cluster (動画)
シャードクラスタでアグリゲーションを実行する場合。
- 単一のシャードのノードで完結する場合ではなく、複数のノードからデータを抽出しソートを行う場合は、ランダムなシャード上に結果を集約し、そのシャード上でソートを実行する
- 実行の前に、オプティマイザがパイプラインを入れ替えたりする(結果は同じであっても)
- データをマージするノードは、ランダムであるがある条件の場合はプライマリシャードを使う
- $out
- $facet
- $lookup
- $graphLookup
- プライマリシャードを利用する場合、プライマリシャードが非力だとパフォーマンスに影響する
Ref. https://docs.mongodb.com/manual/core/sharded-cluster-shards/#std-label-primary-shard
Each database in a sharded cluster has a primary shard that holds all the un-sharded collections for that database. Each database has its own primary shard. The primary shard has no relation to the primary in a replica set.
Check: Aggregation Pipeline on a Sharded Cluster
アグリゲーションを実行する場合で、プライマリシャードを利用するのはどんな時?
- $out, $lookup のオペレーションが発生する時
- コレクションのジョイン(連結)に相当する処理を行うとき
Discussion