1. はじめに
インターネット アプリケーションの普及に伴い、大量のデータの保存とアクセスがシステム設計のボトルネックになっています。大規模なインターネット アプリケーションの場合、毎日数十億の PV がデータベースにかなりの負荷をかけることは間違いありません。これは、システムの安定性と拡張性に大きな問題を引き起こしました。データのセグメンテーションを通じて Web サイトのパフォーマンスを向上させるには、データ層を水平に拡張することがアーキテクチャ開発者にとって推奨される方法となっています。データベースを水平にシャーディングすると、単一マシンの負荷が軽減され、ダウンタイムによる損失を最小限に抑えることができます。負荷分散戦略により、単一マシンのアクセス負荷が効果的に軽減され、ダウンタイムの可能性が軽減されます。クラスター ソリューションにより、読み取りと書き込みの分離によって引き起こされるシングルポイント データベースへのアクセス不能の問題が解決されます。戦略、詳細 アプリケーション内のデータの読み取り (Read) の速度と同時実行性を最大化します。現在、タオバオ、アリババ、テンセントなど、国内の大規模インターネット アプリケーションの多くがこのようなデータ セグメンテーション ソリューションを使用しており、そのほとんどが独自の分散データ アクセス層 (DDAL) を実装しています。実装方法と実装レベルで分けると、JDBC層のカプセル化とORMフレームワーク層の実装の2つのレベル(例としてJavaアプリケーション)に大別できます。 JDBC 層の直接カプセル化に関する限り、中国でよりよく開発されたプロジェクトの 1 つは、アリババ グループの研究機関によって開発され、まだテスト段階にある「Amoeba」と呼ばれるプロジェクトです (ベータ版) ) 運用効率と生産の適時性を検討する必要があります。 ORM フレームワーク レイヤーの実装に関する限り、ibatis と Spring をベースとしたタオバオの分散データ アクセス レイヤーは長年使用されており、その運用効率と生産効率は開発者とユーザーに認められています。この記事は、ORM フレームワーク層に基づく分散データ アクセス層です。このトピックの難しさは、最小限のデータ移行でデータベース容量の拡張 (マシン ノードの追加) の目的をどのように達成するかなど、データベース分割後のルーティング ルールの策定と選択、およびその後のスケーラビリティにあります。主な問題は、ルーティング ルールと、データベース シャードとテーブルの負荷分散戦略を中心に展開します。
2. 基本原則と概念
2.1 基本原則:
人間の認知問題のプロセスは常に次のようなものです。なぜ(なぜ)-?どのように(どのように行うか)、次に、この記事では次の 3 つの問題について説明および調査します:
2.1.1 データセグメンテーションとは
「シャード」という言葉は英語で「断片」を意味し、データベース関連の A大規模多人数参加型オンライン ロールプレイング ゲームで初めて登場したと思われる専門用語。 「シャーディング」を仮に「シャーディング」と呼びます。シャーディングは新しいテクノロジーではありませんが、比較的単純なソフトウェアの概念です。ご存知のとおり、データ テーブルのパーティショニング機能は MySQL5 以降でのみ利用可能でした。それ以前は、MySQL の潜在的なユーザーの多くは、パーティショニング機能があるかどうかがデータベースのスケーラビリティの基準となっていました。インジケーター(もちろん、インジケーターだけではありません)。データベースのスケーラビリティは永遠のテーマです。MySQL プロモーターはよく「単一データベース上のアプリケーション データ処理が拡張され、パーティショニングが必要な場合はどうすればよいでしょうか?」と尋ねられます。その答えは「シャーディング」です。シャーディングは特定のデータベース ソフトウェアに付属する機能ではなく、特定の技術的詳細に基づいて抽象化された水平拡張 (ScaleOut、または水平拡張、外側拡張) のソリューションです。その主な目的は、単一の I/ を突破することです。 O ノード データベース サーバーの容量制限により、データベースのスケーラビリティの問題が解決されます。
一連のセグメント化ルールを通じて、データは異なる DB またはテーブルに水平に分散され、クエリが必要な特定の DB またはテーブルは、クエリ操作の対応する DB ルーティング ルールまたはテーブル ルーティング ルールを通じて見つけられます。ここで言う「シャーディング」は通常、「水平スライス」を指しますが、これがこの記事の焦点でもあります。具体的にどのようなセグメンテーション方法とルーティング方法が使用されますか?この時点で、読者は必然的に疑問を持つでしょう。簡単な例を示します。たとえば、ログ記事 (article) テーブルには次のフィールドがあります:
article_id(int),title(varchar)。 ( 128)),content(varchar(1024)),user_id(int)
このようなテーブルに直面して、どのようにセグメント化すればよいでしょうか?このようなデータを異なるデータベースのテーブルに分散するにはどうすればよいでしょうか?実際、ブログのアプリケーションを分析すると、結論に達するのは難しくありません。ブログのアプリケーションでは、ユーザーは閲覧者とブログ所有者の 2 つのタイプに分けられます。閲覧者がブログを閲覧するとき、実際には特定のユーザのブログを閲覧していることになり、自分のブログを管理するブログオーナーも特定のユーザのブログ(自分の空間)で操作していることになる。いわゆる特定のユーザーは、データベースフィールドで「user_id」として表されます。この「user_id」がサブライブラリの基礎であり、必要なルールの基礎となります。これを行うには、user_id が 1 から 10000 までのすべての記事情報を DB1 の記事テーブルに入れ、user_id が 10001 から 20000 までのすべての記事情報を DB2 の記事テーブルに、というように DBn まで続けます。このようにして、記事データは自然にさまざまなデータベースに分割され、データ分割の目的が達成されます。次に解決すべき問題は、特定のデータベースをどうやって見つけるかということです。実際、問題は単純かつ明白です。データベースを分割するときに識別フィールド user_id を使用したため、データベースのルーティングのプロセスが依然として不可欠であるのは当然です。先ほど紹介したブログ アプリケーションについて考えてみましょう。つまり、他の人のブログにアクセスする場合でも、自分のブログを管理する場合でも、このブログのユーザーが誰であるかを知る必要があります。つまり、このブログの user_id がわかれば、それがわかります。データベースを分割するときのルールは、たとえば、user_id が 234 の場合、その人のルールを使用する場合は、DB1 を見つける必要があります。その人のルールに従って、DB2 を見つける必要があります。同様に、シャーディングのルールを使用して特定の DB に逆ルーティングします。このプロセスは「DB ルーティング」と呼ばれます。
もちろん、データの細分化を考慮したDB設計は、型破りで型破りなDB設計でなければなりません。では、オーソドックスなDB設計とはどのようなDB設計なのでしょうか?
これは基本的に私たちが普段使っているものです。通常、負荷が高い場合は、読み取りと書き込みのスループットとパフォーマンスを向上させるために、関連するレプリケーション メカニズムの使用を検討しますが、これには多くのニーズを満たすことができます。このメカニズム自体はまだ比較的明らかです (後述)。前述した「パラダイムを意識したデザイン」。データ セグメンテーションの DB 設計を考慮すると、この通常のルールと制約に違反します。セグメント化するには、サブデータベースと呼ばれる区別フィールドまたはマーカー フィールドとして使用される冗長フィールドがデータベース テーブル内に存在する必要があります。上記の記事 例の user_id などのフィールド (もちろん、今の例では user_id の冗長性があまり反映されていません。user_id フィールドはデータベースに分割されていなくても表示されるため、それを利用しています) )。もちろん、冗長フィールドの出現はサブデータベースのシナリオにのみ現れるものではなく、多くの大規模なアプリケーションでは冗長性も必要になります。この記事では詳しく説明しません。
2.1.2 なぜデータセグメンテーションが必要なのか
上記でデータセグメンテーションとは何かについて簡単に説明しましたが、読者はなぜデータセグメンテーションが必要なのか疑問に思うかもしれません。 Oracle のような成熟した安定したデータベースは、大量のデータのストレージとクエリをサポートするのに十分ですか?なぜデータのスライスが必要なのでしょうか?確かに、Oracle の DB は非常に成熟していて安定していますが、高額な使用料金とハイエンドのハードウェア サポートをどの企業も負担できるものではありません。年間数千万の使用コストと、数千万ドルかかるミニコンピューターのハードウェア サポートを想像してみてください。これは一般の企業に支払える金額でしょうか。たとえそれを買う余裕があるとしても、より優れたソリューション、より水平方向のスケーラビリティに優れたより安価なソリューションがあるなら、なぜそれを選ばないのでしょうか?
しかし、いつも物足りない。通常、負荷が高い場合は、読み取りと書き込みのスループットとパフォーマンスを向上させるために、関連するレプリケーション メカニズムの使用を検討しますが、これには多くのニーズを満たすことができます。このメカニズム自体はまだ比較的明らかです。まず、その有効性は読み取り操作の割合に依存します。多くの場合、書き込み操作が過負荷になると、マスターはそれを処理できなくなります。スレーブの数も比較的大きくなり、書き込み操作はマスターで実行された後も各スレーブ マシンで実行する必要があるため、CPU の計算能力が非常に高くなります。現時点ではシャーディングが役に立たなくなる可能性があります。レプリケーションは実行できないのに、なぜシャーディングが機能するのでしょうか?理由は簡単で、拡張性が高いからです。各マシンがどれほど適切に構成されているかに関係なく、各マシンには独自の物理的な上限があることがわかっているため、アプリケーションが単一マシンの特定の上限に達したか、それを大幅に超えた場合は、他のマシンに助けを求めるか、アップグレードを続けるしかありません。私たちのハードウェアではありますが、一般的な解決策は、圧力を共有するマシンを追加して水平方向に拡張することです。ビジネス ロジックが成長し続けるにつれて、マシンが直線的な成長を通じて需要に対応できるかどうかも考慮する必要があります。シャーディングにより、コンピューティング、ストレージ、および I/O を複数のマシンに並行して簡単に分散できるため、単一障害点を回避し、システムの可用性を確保し、適切なエラー分離を実行しながら、複数のマシンのさまざまな処理機能を最大限に活用できます。
上記の要因に基づいて、データのセグメント化は非常に必要であり、ここで説明するデータのセグメント化でも背景として MySql が使用されます。コストを考慮して、多くの企業が無料でオープンな MySql を選択しています。 MySQL についてある程度の知識がある開発者は、データ テーブルのパーティショニング機能が MySQL5 以降でしか利用できなかったことを知っているかもしれません。それ以前は、MySQL の潜在的なユーザーの多くが MySQL のスケーラビリティを懸念しており、パーティショニング機能があるかどうかが重要な指標となっていました。 (もちろん唯一の指標ではありません) データベースのスケーラビリティを測定します。データベースのスケーラビリティは永遠のテーマです。MySQL プロモーターはよく「単一データベース上のアプリケーション データが拡張され、分割する必要がある場合、どうすればよいでしょうか?」と尋ねます。これもシャーディングです。プラン。
無料の MySQL と安価なサーバー、または PC を使用してクラスターを作成し、ミニコンピューター + 大規模な商用 DB の効果を実現し、多額の設備投資を削減し、運用コストを削減します。したがって、私たちはシャーディングを選択し、シャーディングを採用します。
2.1.3 データセグメンテーションを実現する方法
データセグメンテーションについて言えば、データセグメンテーションの方法と形式をもう一度詳しく説明します。
データのセグメント化は、一連のセグメント化ルールを通じてさまざまな DB サーバーに分散され、特定のデータベースへのアクセスは、単一のサーバーではなく、ルーティング ルールによってルーティングされます。 N 台のサーバーにより、単一マシンの負荷圧力を軽減できます。
データのセグメント化は、データベース内でも行うことができます。たとえば、article は、article_001、article_002 などのサブテーブルと、いくつかのサブテーブルに分割されます。 -テーブルは水平方向に結合され、論理的に完全な記事テーブルが形成されます。その目的は実際には非常に簡単です。たとえば、現時点では、article テーブルに 5,000 万個のデータがあり、このテーブルに新しいデータを追加 (挿入) する必要があります。挿入が完了すると、データベースによってこのテーブルのインデックスが再作成されます。 5,000 万行のデータが作成されるため、インデックス作成のシステム オーバーヘッドは無視できません。しかし、逆に、このテーブルをarticle_001からarticle_100までの100のテーブルに分割すると、5,000万行のデータが平均され、各サブテーブルには50万行のデータのみが含まれます。このとき、50万行のみのテーブルを追加します。データの挿入後にインデックスを作成する時間が大幅に短縮され、DB の実行効率が大幅に向上し、DB の同時実行性が向上します。もちろん、シャーディングの利点はまだ知られていません。書き込み操作のロック操作など、明らかな利点も数多くあります。
要約すると、サブデータベースはシングルポイントマシンの負荷を軽減し、サブテーブルはデータ操作の効率、特に書き込み操作の効率を向上させます。この記事の執筆時点では、セグメント化の方法についてはまだ触れていません。次に、セグメント化ルールを詳しく説明します。
上記のように、データの水平分割を実現するには、分割の基礎となる冗長な文字が各テーブルに存在する必要があり、一般的なアプリケーションでは、これに基づいて識別フィールドとして user_id を選択します。データベースの分割方法とルールは以下の3つです: (もちろん他の方法もあります)
数値セグメントで分割:
(1) user_id は区別で、1から1000が DB1 に対応し、1001から2000が DB2 に対応します。オン;
利点: 部分的な移行が可能
欠点: 不均一なデータ分散
(2) ハッシュ係数:
user_id をハッシュし (user_id が数値型の場合は user_id の値を直接使用し)、特定の数値を使用します。たとえば、アプリケーションがデータベースを 4 つのデータベースに分割する必要がある場合、数値 4 を使用します。 user_id のハッシュ値はモジュロ計算されます (user_id%4)。この場合、各操作には 4 つの可能性があります。結果が 1 の場合は DB1 に対応し、結果が 2 の場合は DB2 に対応します。結果が 3 の場合は DB3 に対応し、結果が 0 の場合は DB4 に対応します。このようにして、データは 4 つの DB に均等に分散されます。
メリット:データが均等に分散される
デメリット:データ移行が面倒、マシンの性能に合わせてデータを割り当てられない
(3)データベースの構成を認証データベースに保存する
それはDBを作成することであり、このDBはuser_id を DB に個別にマッピングする場合、データベースにアクセスするたびに、まずデータベースにクエリを実行して特定の DB 情報を取得し、その後、必要なクエリ操作を実行する必要があります。
利点: 強力な柔軟性、1 対 1 の関係
欠点: 各クエリの前にもう 1 つのクエリが必要となり、パフォーマンスが大幅に低下します
上記は通常の開発で選択する 3 つの方法ですが、一部の環境では可能な場合があります複雑なプロジェクト これら 3 つの方法を組み合わせて使用します。上記の説明により、サブライブラリのルールも簡単に理解できました。もちろん、サブライブラリーについては、より優れた、より完全な方法が存在するでしょうが、私たちは依然として探索と発見を続ける必要があります。
3. このディスカッションのアイデアの基本的な概要
上記のテキストでは、データベースのセグメンテーションのいくつかの概念と意味、および人間の認知的なもののルールに従ったいくつかの従来のセグメンテーションについて説明しています。簡単に紹介しました。このトピックで説明する分散データ層はそれだけではなく、完全なデータ層ソリューションとはどのようなものですか?次の文章では、この研究テーマの完全なアイデアと実装方法について詳しく説明します。
分散データソリューションが提供する機能は以下の通りです:
(1) シャーディングルールとルーティングルール(RRと呼ばれるRouteRule)を提供し、上記の3つのセグメンテーションルールを直接本システムに埋め込みます。具体的な埋め込み方法については、以下のコンテンツで詳しく説明します
(2) データの高可用性を確保するためにクラスター (グループ) の概念を導入します
(3) 負荷分散戦略 (LoadBalancePolicy と呼ばれます) を導入します。 LB);
(4) シングルポイント マシンの可用性を定期的に検出するクラスター ノードの可用性検出メカニズムを導入し、LB 戦略の適切な実装を確保し、高度なシステムの安定性を確保します。データクエリ速度を向上させるための /write の分離
サブデータベースとサブテーブルのデータ層の設計が十分ではありません。特定のノード上の DB サーバーがダウンするとどうなりますか?はい、データベースのセグメント化スキームを採用しています。これは、完全な DB を形成する N 台のマシンが存在することを意味します。1 台のマシンがダウンしても、DB 内の 1 つの N/N のデータのみにアクセスできます。これは、これが許容されることを意味します。少なくとも分割前の状況よりはかなり改善されており、DB全体にアクセスできなくなることはありません。一般的なアプリケーションでは、このようなマシンの障害によってデータにアクセスできなくなることは許容されます。システムが同時実行性の高い電子商取引 Web サイトの場合はどうでしょうか。単一ノード マシンのダウンタイムによって引き起こされる経済的損失は非常に深刻です。つまり、現在のソリューションにはまだ問題があり、フォールトトレランス性能がテストに耐えられないということです。もちろん、問題には必ず解決策があります。ここではクラスターの概念を導入します。つまり、各サブライブラリ ノードに複数のマシンを導入します。通常の状況では、これらの複数のマシンが負荷を共有します。ダウンタイムが発生した場合、ロード バランサーはダウンしたマシンに負荷を分散します。このようにして、耐障害性の問題は解決されます。そこで私たちはクラスターの概念を導入し、それをフレームワークに埋め込み、フレームワークの一部になりました。
上の図に示すように、データ層全体は Group1、Group2、Group3 の 3 つのクラスターで構成されています。これら 3 つのクラスターは、データを水平に分割した結果です。もちろん、これら 3 つのクラスターもクラスターを形成します。完全なデータが含まれています。各グループには 1 つのマスター (もちろん複数のマスターが存在する可能性があります) と N つのスレーブが含まれており、これらのマスターとスレーブのデータは一貫しています。たとえば、グループ 1 の 1 台のスレーブがダウンした場合、グループ全体のすべてのマシンがダウンしない限り、2 台のスレーブを使用できます。そのようなことが起こる確率は非常に小さいです(停電がない限り、そのようなことは起こりそうにありません)。
クラスターが導入される前のクエリ プロセスは、大まかに次のとおりでした。データ レイヤーをリクエストし、必要なサブデータベース識別フィールド (通常は user_id) を渡します。この決定では、データ レイヤーは識別フィールドに基づいて特定の DB にルーティングされますか? DB内のデータ操作を実行します。これはクラスターが導入されていない状況です。そのときにクラスターが導入されたらどうなるでしょうか。図 1 からわかるように、ルーター上のルールとポリシーは、実際には特定のグループにのみルーティングできます。つまり、仮想グループにのみルーティングできます。このグループは特定の物理サーバーではありません。次に行う必要があるのは、特定のデータ操作を実行する特定の物理 DB サーバーを見つけることです。このリンクのニーズに基づいて、ロード バランサー (LB) の概念を導入しました。ロード バランサーの役割は、特定の DB サーバーを見つけることです。具体的なルールは次のとおりです。 ロード バランサーは、現在の SQL の読み取りおよび書き込み特性を分析し、書き込み操作または強力なリアルタイム パフォーマンスが必要な操作である場合、クエリの負荷をマスターに直接分散します。読み取り操作の場合、負荷分散ポリシー A スレーブを通じて分散されます。当社のロード バランサーの主な研究方向は負荷分散戦略であり、通常、負荷分散にはランダム負荷分散と加重負荷分散が含まれます。ランダム負荷分散は理解しやすいです。つまり、N 個のスレーブからランダムにスレーブを選択します。このようなランダムな負荷分散では、マシンのパフォーマンスは考慮されず、デフォルトでは各マシンのパフォーマンスが同じになります。これが現実の状況であれば、そうすることに何も問題はありません。そうでない場合はどうなりますか?各スレーブ マシンの物理的なパフォーマンスと構成は異なります。パフォーマンスを考慮せずにランダムな負荷分散を使用することは、非常に非科学的です。これにより、マシンのパフォーマンスが低いマシンに不必要に高い負荷がかかり、さらにはダウンタイムのリスクが生じます。同時に、高性能データベース サーバーの物理パフォーマンスを最大限に活用することができません。この考慮に基づいて、私たちは加重負荷分散を導入しました。つまり、システム内の特定のインターフェイスを通じて、各 DB サーバーに重みを割り当てることができ、実行時にクラスター内の重みの割合に応じて LB が割り当てられます。負荷の一定の割合が DB サーバーに与えられます。もちろん、このような概念を導入すると、システムの複雑さと保守性が増大することは間違いありません。利益と損失があり、それから逃れる方法はありません。
サブデータベース、クラスター、ロードバランサーは大丈夫ですか?物事は私たちが思っているほど単純ではありません。これらにより、基本的にデータ層が多大な圧力に耐えられることが保証されますが、そのような設計ではデータベースのダウンタイムの危険を完全に回避することはできません。グループ 1 のスレーブ 2 がダウンしている場合、システムの LB はそれを認識できず、スレーブ 2 が使用可能であると認識して負荷をスレーブ 2 に割り当て続けるため、これは実際には非常に危険です。このようにして問題が発生し、クライアントでは当然データ操作失敗エラーや例外が発生します。これは非常に不親切です!この問題を解決するにはどうすればよいでしょうか?クラスター ノードの可用性検出メカニズムまたは可用性データ プッシュ メカニズムを導入します。これら 2 つのメカニズムの違いは何ですか?まず検出メカニズムについて説明します。名前が示すように、検出はクラスター内の各データベースの可用性を随時試行するデータ層クライアントです。実装の原則は、データベース ポートへの試行的なリンクです。もちろん、JDBC を使用して接続を試みたり、Java の例外メカニズムを使用して可用性を判断したりすることもできます。詳細については、次のテキストで説明します。では、データプッシュメカニズムとは何でしょうか?実際、この問題は実際のアプリケーションのシナリオで議論する必要があります。通常の状況では、適用された DB データベースがダウンしている場合、DBA はその時点でデータベースの現在のステータスを手動でプッシュすることになると思います。クライアント、つまり分散データ層のアプリケーション側には、この時点でローカル DB ステータス リストが更新されます。そして、このデータベース ノードは使用できないことを LB に通知します。このデータベース ノードに負荷を割り当てないでください。 1 つはアクティブな監視メカニズムで、もう 1 つはパッシブな通知メカニズムです。どちらにもそれぞれのメリットがあります。しかし、それらはすべて同じ効果を達成できます。こうすることで、今想定した問題は起こらない、たとえ起こったとしてもその確率は最小限に抑えられます。
上記の本文で言及されているマスターとスレーブについては、あまり詳しく説明していません。図 1 に示すように、グループは 1 つのマスターと N つのスレーブで構成されます。なぜこれを行うのでしょうか?マスターは書き込み操作の負荷を担当します。つまり、すべての書き込み操作はマスターで実行され、読み取り操作はスレーブに割り当てられます。これにより、読み取り効率が大幅に向上します。一般的なインターネット アプリケーションでは、いくつかのデータ調査の結果、読み取り/書き込みの比率は約 10:1 であると結論付けられました。これは、多数のデータ操作が読み取り操作に集中していることを意味します。これが、複数のスレーブの理由がある理由です。しかし、なぜ読み書きを分けるのでしょうか? DB に詳しい研究開発担当者は皆、書き込み操作には行ロック、テーブル ロック、ブロック ロックなどのロックの問題が関係し、システムの実行効率が相対的に低下することを知っています。書き込み操作を 1 つのノードに集中させ、読み取り操作を他の N ノードで実行することで、読み取り効率が効果的に向上し、システムの高可用性が確保されます。読み取りと書き込みを分離すると、たとえば、マスター上のデータをクラスター内の他のスレーブ マシンとどのように同期して整合性を保つことができるかなど、新たな問題も発生します。これはあまり注意を払う必要はありません。 MySql のプロキシ メカニズムはこれを行うのに役立ちます。プロキシ メカニズムはこのトピックとはあまり関係がないため、ここでは詳しく説明しません。
まとめると、このトピックで検討する分散データ層の一般的な機能は次のとおりです。