分散データベースを分割する一般的な方法

要約: この記事では、データベースのセグメンテーションに関する2つのアイデアを紹介します。一般的な理解は、「垂直分割」は「列」が「行」に変更されるのと同じで、「水平分割」は「行」が「列」に変更されるのと同じです。

分散システムでの「スケーラビリティ」の最も重要なことは、最初に「ステートレス」にすることです。これにより、複数のコピーを切り替えるときに混乱を心配することなく、必要に応じて水平方向に「スケーリング」できます。分散システムの懸念-ステートレス」の詳細な説明」でこれについて話します。

ただし、横展開しても本質的には「大きなプログラム」ですが、「コピー可能」になります。

「大規模なプログラム」を排除したい場合は、「セグメント化」する必要があります。セグメント化をうまく行うには、「高凝集性と低結合」のコアアイデアが不可欠です。これは、分散システムの問題-高凝集性と低結合」の詳細な説明のトピックです。

余談:使用できない単一の注文アプリケーションに遭遇した場合、Brother Zによる一般的なアドバイスは、まず「拡張」を検討し、次に「カット」を検討することです。これはコードの記述と同じです。多くの場合、古い関数を変更するよりも、新しい関数を「追加」する方が簡単です。

「拡張」については、最初に「垂直拡張」(ハードウェアを追加してもお金で問題を解決できる)を検討し、次に「水平拡張」(ステートレス変換+マルチノード展開、これは小さな操作です)を検討します。

「スライシング」は一般に「垂直方向の切断」(ビジネスセグメンテーションによると、これは主要な操作です)であり、「水平方向の切断」(実際には、フロントエンドの分離などの単一のアプリケーションでの階層化)の場合もあります。

分散システムフォーカスレジリエントアーキテクチャの第3部では、アプリケーションの「スケーラビリティ」をより高いレベルに引き上げるために、2つの一般的な「疎結合」アーキテクチャモードについて説明しました。

これらはすべてアプリケーションレベルのタスクです。通常の状況下では、アプリケーションレベルで手術を行うことと、キャッシュを最大限に活用することで、システムの開発を長期間サポートできます。特に、データ量は大きくありませんが、大量のリクエストがある「CPU集中型」のシナリオです。

ただし、現在の作業シナリオが特定の規模の非常に成熟したプロジェクトである場合、それを開発するほど、ボトルネックが常にデータベースに表示されます。CPUの長期的な高負荷やダウンタイムなどの現象も発生します。

このようなシナリオでは、データベースを操作する必要があります。今回、Brother Zは、データベースを「スケーラビリティ」にする良い方法についてあなたと話しに来ます。

コアアピール

手術が必要なデータベースに直面すると、システム全体がこのように成長することがよくあります。

先に述べたように、この時のボトルネックはしばしば「CPU」に反映されます。

データベースの場合、ハードディスクとメモリの拡張は、 "増加"して直接使用できるため、比較的簡単です。

CPUは異なります。CPUの使用率が高くなったら、多くてもインデックスが適切に作成されているかどうかを確認します。その後は、基本的に単に監視できます。

したがって、この問題を解決するアイデアは、当然、データベースのCPU負荷を複数のCPUに分散する方法になります。必要に応じていつでも増やすことができます。

次に、これはアプリケーションのように「セグメンテーション」を行うだけではありません。また、分散システムの「分割統治」のアイデアを具現化したものでもあります。

セグメンテーションなので、本質的にはアプリケーションと同じであり、「垂直セグメンテーション」と「水平セグメンテーション」に分けられます。

垂直分割

垂直セグメンテーションは、「縦セグメンテーション」と呼ばれることもあります。

アプリケーションと同様に、「ビジネス」をディメンションとするセグメンテーション方式であり、異なるデータベースサーバー上で異なるビジネスデータベースを実行し、それぞれが独自の義務を果たします。

一般に、Z兄弟は、「水平セグメンテーション」よりも「垂直セグメンテーション」を優先することをお勧めしますプロジェクトでSQLステートメントを自由に開くことができます。「結合」および「トランザクション」のキーワードが多数あるはずです。この種の関連するクエリとトランザクション操作は、本質的に一種の「リレーショナルバインディング」です。データベースの分割に直面した後は、プレイできません

この時点では、2つのオプションしかありません。

  1. 不要な「関係***」ロジックを破棄します。これには、不要な「バッチ操作」サービスを削除するためのビジネス調整が必要か、不要な強力な整合性トランザクションを削除します。しかし、あなたはまた、無限のシーンがいくつかあるに違いないことも知っています。
  2. 「マージ」、「関連付け」、その他のロジックのいずれかが浮かび上がり、ビジネスロジックレイヤーまたはアプリケーションレイヤーのコードにも反映されます。

結局、どのように選択しても、変更は大きなプロジェクトです。

このプロジェクトを可能な限り小さくしてコストパフォーマンスを追求するには、「密接に関連するテーブルを分割しないようにする」という原則に従う必要があります

2つのテーブル間の関係が近いほど、「結合」と「トランザクション」の需要が増えるため、この原則に固執すると、同じモジュールと密接に関連するビジネスがすべて同じライブラリに分類され、 「join」と「transaction」を引き続き使用して作業します。

したがって、「垂直セグメンテーション」アプローチを優先する必要があります。

「垂直セグメンテーション」の考え方は非常に単純です。通常の状況では、セグメント化されたアプリケーションに多かれ少なかれ1対1で対応することをお勧めします

実際の仕事では、「垂直セグメンテーション」は主に「ビジネス」の親しみやすさに反映されているので、ここでは続けません。

「垂直セグメンテーション」の利点は次のとおりです。

1.凝集度が高く、明確な分割ルール。「水平セグメンテーション」と比較すると、データの冗長性は低くなります。

2.アプリケーションと1対1の関係にあり、問題の保守と特定に便利です。特定のデータベースで異常なデータが見つかったら、このデータベースの関連プログラムをチェックするだけで十分です。

ただし、これは「万能」ソリューションではありません。これは、将来のビジネスの発展を誰も予測できないため、最も明らかな欠点は、非常に頻繁にアクセスされる、または大量のデータを持つテーブルのパフォーマンスボトルネックが依然として存在することです

この問題を本当に解決する必要がある場合は、「水平セグメンテーション」から移行する必要があります。

余談:自分を説得することなく、「水平セグメンテーション」を回避するようにしてください。次のコンテンツを読んだ後、理由がわかります。

次に、Z兄弟は、この記事の焦点である「水平セグメンテーション」について良い話をします。

水平分割

「垂直セグメンテーション」を行った後も、データベースに10億を超えるデータを含むテーブルがまだ見つかっていると想像してください。

現時点では、テーブルを「水平に分割」する必要がありますが、これについてどう思いますか?

Z兄弟があなたに教えた一連の考えは次のとおりです。

  1. まず、「最高頻度」の「読み取り」フィールドを見つけます
  2. このフィールドの実際の使用の特性を確認します(他のテーブルの関連フィールドでもあるかどうかなど、複数のバッチクエリまたは複数の単一クエリなど)。
  3. 次に、この特性に基づいて適切なセグメンテーションスキームを選択します。

なぜ高周波の「読み取り」フィールドを最初に見つけるのですか?

実際の使用では、「読み取り」操作は「書き込み」操作よりもはるかに大きいことが多いためです。一般に、「書き込み」の前に「読み取り」による事前検証を行う必要がありますが、「読み取り」には独自の使用シナリオがあります。したがって、より高い頻度の「読み取り」シナリオを考慮する、生成される値は必然的に大きくなります

たとえば、10億のデータテーブルは注文テーブルであり、構造は次のようになります。

order (orderId long, createTime datetime, userId long)

最初にいくつかの「水平セグメンテーション」方法を見てみましょう。次に、どの方法がどの方法に適しているかを理解できます。

範囲の分割

これは「連続的な」セグメンテーション方法です。

たとえば、時間(createTime)セグメンテーションに従って、年と月、order_201901 1つのライブラリー、order_201902 1つのライブラリーなどで分割できます。

注文番号(orderId)に応じて、100,000から199999までの1つのライブラリ、200,000から299999までの1つのライブラリなどが存在します。

このセグメンテーション方法の利点は、単一のテーブルのサイズが制御可能であり、拡張時にデータの移行が必要ないことです

一般に、シリアル番号が近いほど、またはシリアル番号が大きいほど、「新しい」データであるため、アクセスされる頻度と確率は「古い」データよりも多くなります。これにより、主に新しいライブラリに圧力が集中し、履歴が長くなるほど、ライブラリはアイドル状態になります

ハッシュセグメンテーション

「範囲セグメンテーション」とは対照的に、これは「個別の」セグメンテーション方法です。

その利点は、「範囲セグメンテーション」の欠点を解決することであり、新しいデータが各ノードに分散されるため、いくつかのノードに圧力が集中するのを回避できます

同様に、デメリットは「スコープセグメンテーション」のメリットに反し、二次拡張が実行されると、データの移行が必然的に伴います。ハッシュアルゴリズムは固定されているため、アルゴリズムが変更されると、データの分布も変化します。

ほとんどの場合、ハッシュアルゴリズムは単純な「モジュロ」演算で実行できます。次のようになります。

11個のライブラリに分割した場合、式はorderId%10です。

100000%10 = 0、db0に割り当てられます。

100001%10 = 1、db1に割り当てられます。

....

100010%10 = 0、db0に割り当てられます。

100011%10 = 1、db1に割り当てられます。

実際、いくつかのシナリオでは、カスタムID生成(前の記事「分散システムの不可欠な医学-グローバルな一意のドキュメント番号生成」を参照)を使用して、ハッシュセグメンテーションを通じて両方を実現できます。放熱ポイントデータを生成すると、グローバルテーブルへの依存を減らし、特定のデータを見つけることができます。

たとえば、userIdの仮数をorderIdに追加して、orderIdとuserIdの等しいモジュロ結果の効果を達成します。例を挙げましょう。

ユーザーのuserIdは200004です。仮数を4ビットとると、ここでは4になり、0100で表されます。

次に、カスタムIDアルゴリズムによってorderIdの最初の60桁を生成し、後ろに0100を追加します。

したがって、orderId%10とuserId%10の結果は同じです。

もちろん、userId以外の要素を追加するのは簡単ではありません。つまり、グローバルテーブルを増やすことなく、1つの追加のディメンションをサポートできます。

グローバルテーブルは2回言及されています。グローバルテーブルとは何ですか。

グローバルテーブル

この方法は、セグメンテーションの基礎として使用されるパーティションキーと、個別のライブラリまたはテーブルに対応する各特定のデータのIDを保存することです。たとえば、次のようなテーブルを追加するには:

1     nodeId    orderId
2      01          100001
3     02          100002
4     01          100003
5     01          100004...
6    ...

このように、特定のデータのほとんどが異なるサーバーに分散されていることは事実ですが、このグローバルテーブルは、「形状が散らばっていない」という感覚を人々に与えます。

データを要求するときに、必要なデータが存在するサーバーを直接見つけることはできないため、各操作では、最初にこのグローバルテーブルにクエリを実行して、特定のデータが格納されている場所を知る必要があります。

この「集中型」モデルの副作用は、ボトルネックとリスクがこのグローバルテーブルに転送されることです。ただし、ロジックは単純です。

では、これらのセグメンテーションスキームを選択するにはどうすればよいでしょうか。

ブラザーZからのアドバイスは、ホットデータが特に集中しているシーンでない場合は、最初に「範囲セグメンテーション」を使用することをお勧めしますそうでない場合は、他の2つを選択してください

他の2つを選択する場合、データ量が多いほど、ハッシュセグメンテーションを選択する傾向があります全体的な使いやすさとパフォーマンスの点で後者は前者よりも優れているため、実装コストは高くなります。

「水平セグメンテーション」は本当に「無限に拡大」することができますが、それに対応する欠点があります。

1)バッチクエリ、ページングなどは、追加の作業を行う必要があります。特に、テーブルに複数の高周波フィールドが存在する場合は、where、order by、group byのいずれかになります。

2)分割ルールは「垂直分割」ほど明確ではありません。

ではもう1つの「ナンセンス」を考えてみましょう。完璧な計画はありませんが、特定のシナリオと組み合わせて選択する必要がある適切な計画があります(あなたはメッセージ領域であなたの疑問を提起し、ブラザーZと議論することを歓迎します)

実装方法

「水平セグメンテーション」を具体的に実装すると、「サーフェス」レベルまたは「ライブラリ」レベルの2つのレベルに移動できます。

テーブル

テーブルは同じデータベースに分割され、テーブル名はorder_0、order_1、order_2 .....です。

過剰な単一テーブルデータの問題は解決できますが、CPU負荷の問題は解決できません。したがって、CPUの負荷はそれほど大きくないが、テーブルが大きすぎるためにSQL操作の実行が遅い場合は、この方法を選択できます。

図書館

現時点では、テーブル名は変更できず、すべて順序と呼ばれますが、10個のライブラリに分割されています。次に、db0-user db1-user db2-user .......

このモデルについては前のセクションで説明しているので、これ以上は触れません。

テーブル+ライブラリ

データベースとテーブルを分割することも可能です。たとえば、最初に10個のライブラリを分割し、次に各ライブラリを10個のテーブルに分割します。

これは実際にはセカンダリインデックスのアイデアです。最初のポジショニングは、特定のリソース消費を削減するためにライブラリを通じて実行されます。

たとえば、最初にデータベースを年で分割し、次にテーブルを月で分割します。このようにして、取得する必要のあるデータが数か月間ではなく、数か月間である場合、データベース間の操作を行わずに、単一のライブラリで集計操作を実行してデータを完成させることができます。

ただし、どちらの方法を選択しても、多かれ少なかれ次の2つの問題に直面し、逃げることはできません。

  1. データベース間の結合。
  2. グローバルな集計または並べ替え操作。

最初の問題を解決する最良の方法は、プログラミングの考え方を変えることです。ロジック、リレーションシップ、制約などをアプリケーションコードに反映させ、SQLでこれらのことを行うのを避けてください。

結局のところ、コードは「ステートレス」として記述でき、いつでも拡張できますが、SQLはデータに従い、データは「ステート」であり、当然拡張につながりません。

もちろん、2番目に優れているのは、冗長なグローバルテーブルも処理できることです。このように、これは「データ整合性」作業の大きなテストであるだけでなく、多くのストレージリソースも消費します。

2番目の問題の解決策は、元の集約またはソートを2つの操作に変えることです。複数のノードのトラバーサルは、「並列」方式で実行できます。

それでは、プログラムはデータをセグメント化した後でどのように使用しますか?これは、「処理中」と「処理外」の2つのモードに分けることができます。

「インプロセス」は、パッケージ化されたDALアクセスフレームワーク、ORMフレームワーク、またはデータベースドライバーで実行できます。Aliのtddlなど、このモデルのよく知られたソリューション。

「アウトプロセス」はプロキシモデルです。このモデルのよりよく知られているソリューションは、mycat、cobar、アトラスなどです。このモデルはアプリケーションへの「侵入」が少なく、「データベース」ただし、追加のネットワーク通信により、パフォーマンスがさらに低下します。

古いルールについて、いくつかのベストプラクティスを共有させてください。

ベストプラクティス

まず、マシンを停止することなく、データセグメンテーションに関する2つのヒントを共有します。水平分割のためのハッシュメソッドの実装例を見てみましょう。

初めてセグメンテーションを行う場合、新しく追加されたノードを完全なリアルタイム同期の「マスタースレーブ」の形式で元のノードのコピーとして使用できます。

次に、これに属していないデータを削除します。(もちろん、それを削除しないことには何の問題もありません、それは単により多くのスペースをとります)

このようにして、ダウンタイムはありません。

2つ目は、時間の経過とともにフォローアップを維持できず、2番目のセグメンテーションが必要な場合は、2の倍数を使用して拡張することを選択できます。

このようにして、データの移行は非常に簡単になり、部分的な移行のみが必要であり、考え方は最初のセグメンテーションと同じです。

もちろん、選択したセグメンテーション方法が「範囲セグメンテーション」の場合、2番目のセグメンテーションで問題はなく、データは自然に最新のノードまで実行されます。たとえば、テーブルを年と月で分割するとします。2019年3月のデータは、当然xxxx_201903の表に含まれます。

この時点で、Brother Zは、特に分割できず分割できない場合は、まず「読み取り/書き込み分離」などのソリューションを使用して、最初に直面している問題に対処できることを特に強調したいと考えています

本当にセグメンテーションを実行したい場合は、まず「垂直セグメンテーション」を行い、次に「水平セグメンテーション」を検討する必要があります

一般的に言えば、この順番で考えるとコストパフォーマンスが良いです。

総括する

まとめましょう。

今回はまず、データベースのセグメンテーションに関する2つのアイデアを紹介します。「垂直分割」は「列」が「行」に変化せずに変化することと「水平分割」は「行」が「列」に変化せずに変化することと同じです。

次に、「水平セグメンテーション」の3つの実装方法と具体的な実装のアイデアに焦点を当てました。

最後に、私はあなたにいくつかの実際的な経験を共有しました。

あなたに刺激を与えることを願っています。

関連記事:

おまけ!HUAWEI CLOUDの公式デベロッパープロモーションおよび採用計画が進行中です。クリックして詳細をご覧ください

 

クリックしてフォローし、Huawei Cloudの新しいテクノロジーについて初めて学びます〜

おすすめ

転載: blog.csdn.net/devcloud/article/details/108711190