👌

ひとりMongoDB University / M201 MongoDB Performance(8)

2021/11/09に公開

この記録は、アドベントカレンダー形式ではじめた、MongoDB Universityの学習コースの記録の続きになります!

ただいまのコース

このコースでは、開発者 / オペレーション担当者双方向けの中級レベルの内容とのことです。
前回の記事は、ひとり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つだけになってしまう
      - こういった場合は、ほかにカーディナリティが高いフィールドと組み合わせ、複合シャードキーとする方法がある
    • 例:住んでいる州と、名前の組み合わせなど

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 の場合は、各ノードとのハートビートメッセージから、最も近いノードを判断

https://twitter.com/akiko_pusu/status/1459171808242851849?s=20

  • セカンダリノードからの読み取りでは、まだデータが反映されていない 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