MongoDB をスケールするにはどうすればよいですか?シャーディングのベストプラクティスは何ですか?
柔軟なスキーマにより、ほとんどの人は MongoDB に慣れることができますが、非常に大規模なデータ セットを処理するのに最適なデータベースの 1 つでもあります (日常のアプリケーションに関して言えば、おそらく 最適 です)。この議論を正当化するには、それ自体で 1 つの記事が必要ですが (いつか時間を見つけて書きたいと思います!)、一般的な考え方は、SQL ベースのソリューションはシャーディングをサポートしておらず、シャーディングを構築するのは大変だということです。
最善の策は、クラスターを作成するか (ちなみに、基本的にシャーディングとは何の関係もありません)、Amazon の RDS や Google の Cloud SQL などのマネージド ソリューションを使用することですが、これらはデータが増大するにつれて法外に高価になります。
この記事では、 データベースの水平スケーリングに不可欠な手法の 1 つである MongoDB のシャーディング について説明し、そのベスト プラクティスをいくつか推奨します。ただし、MongoDB のスケーリングを検討している人の多くはシャーディングにあまり慣れていない可能性があるため、シャーディングの基本から始めるのが良いと思います。
ただし、シャーディングについてご存知の場合は、次のセクションをざっと読んでください。
シャーディングの基本
前のセクションの最後の段落で「水平」という言葉が使われていることに気づいたかもしれません。再び大きな回り道をすることなく、この点をすぐに取り上げたいと思います。スケーリングには 2 つのタイプがあると考えられています。ストレージ容量が大きい、より強力なマシンを入手する ( 垂直 ) か、または複数の小型コンピューターを接続してコレクションを形成する ( 水平 ) かのいずれかです。
さて、現在最高のサーバーであっても 256 GB を超える RAM や 16 TB のハードディスクを搭載していないことを考えると、垂直方向にスケール (用語で言えば「スケールアップ」) しようとすると、すぐに壁にぶつかります。ただし、(少なくとも理論的には)任意の数の単一マシンを接続して、この制限を簡単に回避できます。
もちろん、現在の課題はこれらすべてのマシンを調整することです。
データベースのシャーディング
「シャーディング」という用語は一般的にデータベースに適用され、単一のマシンではすべてのデータを保持するのに十分ではないという考え方が使われます。シャーディングすると、データベースは異なるマシン上に存在する個別のチャンクに「分割」されます。簡単な例としては、次のようなものがあります。企業に最大 200 万件の顧客データ項目を保存できるマシンがあるとします。現在、ビジネスはその限界点に達しており、間もなくユーザー数が 250 万人を超える可能性があります。そこで、データベースを 2 つに分割することにしました。

そして魔法のように、システム容量は 2 倍になりました。
そうですね、人生がこんなにシンプルだったらいいのに! 🙂
データベースシャーディングの課題
シャーディングについてもう少し深く考えているとすぐに、いくつかの邪悪な課題が醜い頭をもたげます。
主キーがありません
単一のデータベースから離れるとすぐに、主キーは意味を失います。たとえば、主キーが自動インクリメントに設定されており、データの半分を別のデータベースに移動すると、主キーごとに 2 つの異なるデータ項目が存在することになります。
外部キーがありません
データベースでは、現在のデータベースの外部のエンティティを指すことはサポートされていないため (同じマシン上の別のデータベースですらサポートされていないため、 別の マシン上のデータベースのことは忘れてください)、外部キーの概念は次のように無視されます。良い。突然、データベースが「無能」になり、データの整合性が問題になります。
奇妙なデータエラー
1 台のマシンが故障した場合、エンドユーザーには「おっと、何かが壊れた!」というメッセージが表示される可能性があります。このページには間違いなくイライラするでしょうが、しばらくすれば人生は軌道に乗るでしょう。
次に、シャードデータベースで何が起こるかを考えてみましょう。前の例のシャード データベースが銀行データベースであり、ある顧客が別の顧客に送金しているとします。また、最初の顧客のデータが最初のシャードに存在し、2 番目の顧客のデータが 2 番目のシャードに存在すると仮定します (これでどこに行くかわかりますか?!)。 2 番目のシャードを含むマシンに障害が発生した場合、システムがどのような状態になるか想像できますか?取引金はどこへ行くのでしょうか?最初のユーザーには何が表示されるでしょうか? 2 番目のユーザーには何が表示されますか?シャードがオンラインに戻ったとき、両者は何を目にするでしょうか?
トランザクション管理
トランザクション管理という常に重要なケースについても考えてみましょう。今回は、システムが 100% 正常に動作していると仮定します。ここで、2 人 (A と B) が 3 人目 (C) に支払いを行います。両方のトランザクションが C の口座残高を同時に読み取る可能性が非常に高く、この混乱が生じます。
- C の口座残高 = 100 ドル。
- A のトランザクションでは C の残高が読み取られます: $100。
- B のトランザクションでは C の残高が読み取られます: $100。
- A のトランザクションにより $50 が追加され、残高が更新されます: $100 + 50 = $150。
- B のトランザクションにより $50 が追加され、残高が更新されます: $100 + 50 = $150。
くそ! 50 ドルがあっという間に消えてしまいました。
従来の SQL システムでは、トランザクション管理が組み込まれているため、このような事態は避けられますが、1 台のマシンから出るとすぐに終わりです。
重要なのは、このようなシステムでは、回復不可能なデータ破損の問題に簡単に遭遇するということです。髪を引っ張っても意味がありません。 🙂
MongoDB シャーディング
ソフトウェア アーキテクトにとって、MongoDB に対する興奮は、その柔軟なスキーマではなく、組み込みのシャーディング サポートにありました。いくつかの簡単なルールとマシンを接続するだけで、すぐにシャード MongoDB クラスターを実行する準備が整いました。
以下の図は、これが典型的な Web アプリのデプロイメントでどのように見えるかを示しています。

MongoDB シャーディングの最も優れた点は、シャードのバランス調整さえも自動的に行われることです。つまり、5 つのシャードがあり、そのうちの 2 つがほぼ空である場合、すべてのシャードが均等に満たされるようにバランスを再調整するように MongoDB に指示できます。
開発者または管理者は、MongoDB がバックグラウンドで面倒な作業のほとんどを実行するため、あまり心配する必要はありません。ノードの部分的な障害についても同様です。レプリカ セットが正しく構成され、クラスター上で実行されている場合、部分的な停止がシステムの稼働時間に影響することはありません。
全体を説明するとかなり短くなってしまうので、MongoDB にはシャーディング、レプリケーション、リカバリのためのいくつかの組み込みツールがあり、開発者が大規模なアプリケーションを非常に簡単に構築できることを述べて、このセクションを閉じます。 MongoDB のシャーディング機能に関するより包括的なガイドが必要な場合は、 公式ドキュメント が最適です。
シャーディングを実装するには、この実践的なガイドを確認してください。
この 完全な開発者ガイド にも興味があるかもしれません。
MongoDB シャーディングのベスト プラクティス
MongoDB はそのままシャーディングに「機能」しますが、これで満足できるわけではありません。シャーディングは、プロジェクトの完成度に応じて、プロジェクトを永久に成功させることも失敗させることもできます。
さらに、説明すべき細かい点がたくさんあり、それが失敗すると、プロジェクトが崩壊するのを見るのは珍しいことではありません。その目的は、あなたを怖がらせることではなく、計画の必要性と、たとえ小さな決断であっても細心の注意を払うことの必要性を強調することです。
シャーディング キーは必然的に MongoDB のシャーディングを制御するため、調査をそこから始めるのが理想的です。
高いカーディナリティ
カーディナリティとは変動量を意味します。たとえば、100 万人のお気に入りの国を集めたものはバリエーションが少なくなりますが (世界には国が限られています!)、その電子メール アドレスのコレクションは (完全に) 高いカーディナリティを持ちます。なぜそれが重要なのでしょうか?ユーザーの名に基づいてデータを分割する単純なスキームを選択したとします。

ここではかなり単純な配置になっています。受信ドキュメントはユーザー名をスキャンされ、英語のアルファベットの最初の文字がどこにあるかに応じて、3 つのシャードのいずれかに分類されます。同様に、ドキュメントの検索も簡単です。たとえば、「Peter」の詳細は 2 番目のシャードに確実に含まれます。
それはすべて良いように聞こえますが、重要なのは、受信するドキュメントのユーザーの名前を制御できないということです。ほとんどの場合、B ~ F の範囲の名前しか取得されない場合はどうなるでしょうか?その場合、shard1 にはいわゆる「ジャンボ」チャンクが存在することになります。ほとんどのシステム データがそこに密集し、実質的にセットアップが単一のデータベース システムになります。
治療法?
ユーザーの電子メール アドレスなど、カーディナリティの高いキーを選択します。また、複数のフィールドを組み合わせた複合シャード キーを選択することもできます。
単調変化
MongoDB シャーディングでよくある間違いは、単調増加する (または自動増加する) キーをシャード キーとして使用することです。
通常、ドキュメントの主キーが使用されます。ここでの考え方は良い意味であり、新しいドキュメントが作成され続けると、それらのドキュメントは利用可能なシャードの 1 つに均等に分類されます。残念ながら、このような構成は典型的な間違いです。これは、シャード キーが常に増加している場合、ある時点を過ぎるとシャードの高価値側にデータが蓄積され始め、システムの不均衡が生じるためです。

画像からわかるように、20 の範囲を超えると、すべてのドキュメントがチャンク C に集まり始め、そこでモノリスが発生します。解決策は、ハッシュされたシャーディング キー スキームを使用することです。これは、提供されたフィールドの 1 つをハッシュし、それを使用してチャンクを決定することによってシャーディング キーを作成します。

ハッシュ化されたシャード キーは次のようになります。
{
"_id" :"6b85117af532da651cc912cd"
}
。 。 。以下を使用して Mongo クライアント シェルで作成できます。
db.collection.createIndex( { _id: hashedValue } )
シャードアーリー
トレンチから直接得られた最も有用なアドバイスの 1 つは、たとえ最終的に小さな 2 チャンクのクラスターになったとしても、早めにシャード化することです。データが 500 GB かそこらを超えると、MongoDB でのシャーディングは面倒なプロセスになり、厄介な事態が発生することを覚悟しておく必要があります。さらに、再バランスのプロセスは大量のネットワーク帯域幅を消費するため、注意しないとシステムが停止する可能性があります。
ただし、誰もがシャーディングに賛成しているわけではありません。興味深い例として (実際にコメントに学びがあります)、この素晴らしい Percona ブログを 参照してください。
バランサーの実行
もう 1 つの良いアイデアは、トラフィック パターンを監視し、トラフィックが少ない時間帯にのみシャード バランサーを実行することです。すでに述べたように、リバランス自体にはかなりの帯域幅が必要となるため、システム全体がすぐに停止してしまう可能性があります。不均衡なシャードは直ちにパニックを引き起こす原因ではないことに注意してください。通常の使用を継続し、トラフィックが少ない機会を待って、残りはバランサーに任せてください。
これを実現する方法は次のとおりです (午前 3 時から午前 5 時までトラフィックが少ないと仮定します)。
use config
db.settings.update(
{ _id: "balancer" },
{ $set: { activeWindow : { start : "03:00", stop : "05:00" } } },
{ upsert: true }
)
結論
データベースのシャーディングとスケーリングは難しい作業ですが、ありがたいことに、MongoDB を使用すると、他の一般的なデータベースよりも管理しやすくなります。
確かに、MongoDB がどのプロジェクトにとっても適切な選択ではなかった時代がありました (いくつかの重大な問題とデフォルトの動作のおかげで) が、それらはとうに過ぎ去りました。シャーディング、リバランス、自動圧縮、集約レベルの分散ロック、およびそのような多くの機能に加えて、MongoDB ははるかに進歩しており、今日ではソフトウェア アーキテクトの最初の選択肢となっています。
この記事で、MongoDB のシャーディングとは何か、そして開発者がスケールを目指す際に気をつけなければならないことについて少しでも光を当てることができれば幸いです。次に、一般的な MongoDB コマンドに慣れてください。