サービスの高同時実行性、高パフォーマンス、高可用性の実装
ソフトウェア開発の 3 つの重要な指標: 高い同時実行性、高いパフォーマンス、および高い可用性。
高い同時実行性には 100,000 を超える QPS が必要です。高いパフォーマンスにはリクエストの遅延が 100 ミリ秒未満である必要があります。高可用性は 99.99% (4 ナイン)より高いです。
1. 高い同時実行性:
高い同時実行性は、インターネット分散フレームワークの設計で考慮する必要がある要素の 1 つです。これにより、システムが多くのリクエストを同時に処理できることが保証されます。高い同時実行性の指標は次のとおりです。
1. 応答時間: システムが受信リクエストに応答するのにかかる時間。たとえば、ページを開くのに 1 秒かかります。この 1 秒が応答時間です。
2. スループット: スループットとは、1 秒あたりに処理できるリクエストの数を指します。
3. 1 秒あたりのクエリ レート (QPS、1 秒あたりのクエリ): 1 秒あたりの応答リクエストの数。スループットに似ています。
4. 同時ユーザー数: システム機能を同時に通常使用するユーザーの数。たとえば、インスタント メッセージング システムでは、同時オンライン ボリュームはシステムの同時ユーザーの数をある程度表します。
QPS を向上させるためのアーキテクチャ戦略
Redis、MQ、マルチスレッド
1. 負荷分散:
高い同時実行性を実現するための推奨ソリューションはクラスター化された展開です。1 台のサーバーで実行できる QPS は制限されており、複数のサーバーの重ね合わせ効果は大幅に向上します。
クラスター展開の場合、トラフィックをサーバークラスターに転送する方法を検討する必要があり、ここではLVS (Linux Virtual Server) や nginx などの負荷分散を使用する必要があります。
一般的に使用される負荷分散アルゴリズムには、ラウンドロビン方式、ランダム方式、ソースアドレスハッシュ方式、加重ラウンドロビン方式、加重ランダム方式、最小接続方式などが含まれます。
ビジネスの実践: 数千万のトラフィックを伴うスパイク ビジネスの場合、1 つの LVS ではトラフィックのピークを処理できず、通常は約 10 の LVS が必要であり、ドメイン名解決の負荷分散には DDNS (ダイナミック DNS) が使用されます。高性能ネットワーク カードを使用すると、1 つの LVS で 100 万を超える同時機能を提供できます。
LVS はネットワーク レイヤ 4 プロトコルの転送を担当し、HTTP プロトコルのリクエスト パスに従って負荷分散を実行できないため、Nginx も必要であることに注意してください。
2. プーリング技術:
単一のコネクションを多重化するだけでは高い同時実行性は実現できず、リクエストごとにコネクションを作成したり閉じたりすると、TCP の 3 ウェイ ハンドシェイクや 4 ウェイ ハンドシェイクを考慮すると、多大なオーバーヘッドが発生します。
プーリング技術の中核は、リソースの「事前割り当て」と「リサイクル」であり、一般的に使用されるプーリング技術には、スレッド プール、接続プール、プロセス プール、オブジェクト プール、メモリ プール、コルーチン プールなどがあります。
接続プールのいくつかの重要なパラメータ: 最小接続数、アイドル接続数、最大接続数
3. トラフィックファネル (風制御遮断):
上記の方法は、システムの QPS を改善するための積極的な方法であり、逆転の発想で減算を行ったり、不正なリクエストを遮断したり、通常のビジネス リクエストのためにコア機能を残したりすることもできます。
同時多発インターネット トラフィックはすべてが純粋なわけではなく、多くの悪意のあるトラフィック (ハッカー攻撃、悪意のあるクローラー、ダフ屋、セキラーなど) も存在するため、違法、不適格、優先度の低いトラフィックをブロックするトラフィック インターセプターを設計する必要があります。トラフィックはフィルタリングされ (風制御)、システムへの同時負荷が軽減されます。
インターセプターの階層化:
ゲートウェイとWAF(Webアプリケーションファイアウォール、Webアプリケーションファイアウォール)
攻撃者の送信元 IP のブロック、不正なパラメータを含むリクエストの拒否、送信元 IP によるトラフィックの制限、ユーザー ID によるトラフィックの制限などの方法を使用します。
リスク分析。ビッグ データ機能を使用して、注文などのビジネス データの履歴を分析し、同じ IP を持つ複数のアカウントでの注文や注文後の支払いが早すぎるなどの行動を効果的に特定し、ビジネス チームが使用するアカウントにマークを付けます。
各ダウンストリーム Tomcat インスタンスはローカル メモリ キャッシュを適用し、事前検証のために一部のインベントリをローカルに保存します。もちろん、データの一貫性を可能な限り維持するために、Redis から最新のインベントリ データを定期的に取得し、ローカル メモリ キャッシュに更新するスケジュールされたタスクがあります。
4. リレーショナル データベースを直接読み書きするのではなく、キャッシュを直接読み書きする
MySQL がサブデータベースとサブテーブル、読み取りと書き込みの分離、完全な接続プール構成などを使用したとしても、10W を超える qps の影響には耐えられません。
メモリ キャッシュ、キャッシュの予熱、マルチレベル キャッシュ (JVM キャッシュ、続いて Redis) の読み取り、メッセージ キューの書き込み、最後に MySQL への書き込みを使用する必要があります。
5. マルチレベルキャッシュ
現在、Redis はキャッシュの第一の選択肢です。単一マシンは 60,000 ~ 80,000 qps に達します。高い同時実行性に直面して、ワイヤレスで qps が増加する可能性があるシナリオに対処するために、手動で容量を水平方向に拡張できます。ただし、このアプローチには欠点もあります。Redis はシングルスレッドであり、ホットスポットが発生するためです。
Redis は内部的に crc16 アルゴリズムを使用してハッシュ分割を実行しますが、同じキーが別のマシンに置かれるため、マシンの負荷が増加します。Redis には通常、キャッシュの破壊とキャッシュの侵入という 2 つの問題があり、特に seckill のシナリオでは顕著です。 、ホットスポットの問題を解決したい場合は、さらに困難になります
このとき、マルチレベルのキャッシュを考慮する必要があります。典型的なスパイク シナリオでは、セールが開始された瞬間に 1 つの SKU 製品の QPS が急激に上昇します。このとき、memeryCache を使用してレイヤーをブロックする必要があります。 memeryCache はマルチスレッドであり、redis よりも優れた同時実行性を備えており、ホットスポットの問題を自然に解決できます。memeryCache では、速度を上げるためにメモリを交換する方法である localCache (ローカル キャッシュ) も必要です。ローカル キャッシュはユーザーの最初のレイヤー リクエストにアクセスします。それが見つからない場合は、memeryCache に移動し、次に redis に移動します。このプロセスでは、数百万の QPS がブロックされる可能性があります
6. マルチスレッド化
マルチスレッド同時処理により処理速度が向上、CountDownLatch
7. I/Oの最適化
たとえば、ネットワーク IO を削減するために、複数の単一リクエストが 1 つのバッチ リクエストに最適化されます。
MySQLに対応するのはバッチインサート、バッチクエリ
接続の確立、データのやり取り、接続の解放のたびに大量のリソースが消費されるため、ユーザー状態からコア状態への切り替えも必要になります。
8. ログをエレガントに印刷する
同時実行性が高い場合、不適切なログ出力によりプログラムの IO が占有され、応答時間が増加します。ログのボリュームが大きすぎると、ディスクがいっぱいになってしまいます。
①: ログを非同期に印刷し、ログ出力の長さを制御します
②: ホワイトリストベースのログ印刷、ホワイトリストユーザーのログ印刷要求をオンラインで設定し、大量の無効なログ出力を回避します。
他の:
マシンの拡張:
大規模なトラフィックが到着する前に、サービス マシンの容量を拡張してトラフィックを分割して消化します。
たとえば、Redis キャッシュは 1 台のマシンで 6 ~ 8 W qps に達することがありますが、同時実行性が高くなる前に、qps が無限に増大するシナリオに対処するために手動または自動スケーリングと拡張を構成できます。
同時実行性の高いサービスの相違:
qps が 10W で、各リクエストが 1KB のデータを読み書きすると仮定すると、10W のリクエストは 1 秒あたり 1GB、1 分あたり 60GB の読み書きが可能となり、基盤となるデータ ストレージとアクセスに大きな負荷がかかります。
2.高性能:
パフォーマンスが高いとは、プログラムの処理速度が非常に速く、占有メモリが少なく、CPU 使用率が低いことを意味します。高パフォーマンスのインジケーターは、高同時実行性のインジケーターと密接に関連していることが多く、パフォーマンスを向上させたい場合は、システムの同時実行機能を向上させる必要があり、この 2 つは密接に関連しています。
システムのパフォーマンスに影響を与える要因は何ですか?
業務コードの論理設計、アルゴリズム実装が効率的か、アーキテクチャ設計
業務システムのCPU、メモリ、ディスクなどの性能
ダウンストリームシステムのパフォーマンス
サービスリンクの長さ
リクエスト/レスポンスのパケットサイズ
ユーザーのネットワーク環境
パフォーマンスを向上させるにはどうすればよいですか?
1. IO ブロックによる CPU のアイドル状態を回避し、CPU を無駄に消費します。
システムが大量のディスク IO 操作を処理する場合、CPU とメモリの速度がディスクの速度よりもはるかに高いため、CPU はディスクから処理結果が返されるまでの待機に時間がかかりすぎる可能性があります。IO のオーバーヘッドのこの部分は、「iowait」と呼ばれます。
ディスクには、IOPS、つまり 1 秒あたりの読み取りと書き込みの数というパフォーマンス指標があり、より優れたパフォーマンスのソリッド ステート ドライブの場合、IOPS は約 30,000 になります。seckill システムの場合、単一ノードの QPS が 100,000 で、各リクエストが 3 つのログを生成する場合、ログ書き込み QPS は 30W/s となり、ディスクはそれをまったく処理できません。
Linux には、オペレーティング システムによって管理されるメモリベースのファイル システムである tmpfs (一時ファイル システム) という特別なファイル システムがあります。ディスクに書き込むと、実際にはメモリに書き込まれます。ログ ファイルが設定したしきい値に達すると、オペレーティング システムはログをディスクに書き込み、tmpfs 内のログ ファイルを削除します。
このバッチおよびシーケンシャル書き込みにより、ディスクのスループット パフォーマンスが大幅に向上します。
2. 同期を確保するために複数のスレッド間にロックを追加しないようにします。その結果、並列システムが直列化されます。
3. オペレーティング システムがスケジューリング時にリソースを浪費する原因となる、あまりにも多くのプロセスとスレッドを作成、破棄、維持することを避けます。
4. Redis などの高性能キャッシュ。
キャッシュからホットスポット データを読み取ることで、ホットスポット データのアクセス パフォーマンスが向上し、毎回データベースからホットスポット データを読み取ることがなくなり、データベースに負荷がかかることがなくなります。
1.ロックフリー
ほとんどの場合、マルチスレッド処理により同時実行パフォーマンスが向上します。
1): シリアルロックフリー
ロックフリー シリアルの最も単純な実装は、redis/Nginx がこの方法を採用しているなど、シングルスレッド モデルです。
ネットワーク プログラミング モデルでは、メイン スレッドは IO イベントの処理を担当します。メイン スレッド MainReactor が新しい接続を受け入れると、多くの SubReactor から 1 つを選択して登録し、Channel を作成して IO スレッドとバインドします。接続の読み取りと書き込みはすべて同じスレッドで実行され、同期は行われません。
マスター/スレーブ Reactor 責任連鎖モデル:
2): 構造ロックフリー
ロックフリーのデータ構造は、CAS アトミック操作などのハードウェアでサポートされるアトミック操作を使用して実装できます。
2. ゼロコピー
3. 連載
シリアル化技術は、データがファイルに書き込まれ、ネットワーク経由で送信され、データが読み取られるときに逆シリアル化されるときに必要になることがよくあります。
送信データの表現として、シリアル化はネットワーク フレームワークや通信プロトコルから切り離されます。たとえば、ネットワーク フレームワーク taf は jce、json、カスタム シリアル化をサポートし、HTTP プロトコルは XML、JSON、ストリーミング メディア送信などをサポートします。
1) シリアル化された分類
①:内蔵タイプ
Java の java.io.Serializable など、プログラミング言語でサポートされる型を指します。このタイプは言語のバインディングのため汎用的ではなく、一般にパフォーマンスが低く、通常はローカルでのみ使用されます。
②:テキストタイプ
一般に、XML や JSON などの標準化されたテキスト形式です。このタイプは読みやすく、クロスプラットフォームをサポートし、幅広いアプリケーションを備えています。主な欠点は、比較的肥大化し、ネットワーク送信に多くの帯域幅が消費されることです。
③:バイナリ型
バイナリ エンコーディングを使用すると、データ構成がよりコンパクトになり、複数の言語とプラットフォームがサポートされます。一般的なものは、Protocol Buffer/Thrift/MessagePack/FlatBuffer などです。
2) 業績指標
シリアル化/逆シリアル化を測定するには、次の 3 つの主要な指標があります。
①:シリアル化後のバイトサイズ。
②: シリアライズ/デシリアライズ速度。
③:CPUとメモリの消費量
その中で最もパフォーマンスが良いのは FlatBuffer で、次に Protobuf が続きます。
3) 選択の考慮事項
①:パフォーマンス
CPU とバイト フットプリントはシリアル化の主なオーバーヘッドです。基本的な RPC 通信、ストレージ システム、および同時実行性の高いサービスには、高性能かつ高圧縮のバイナリ シリアル化を選択する必要があります。Web リクエストが少ない一部の内部サービスとアプリケーションはテキストの JSON を使用でき、ブラウザーは JSON を直接サポートします。
②:使いやすさ
豊富なデータ構造と補助ツールにより、使いやすさが向上し、ビジネス コードの開発量が削減されます。現在、多くのシリアル化フレームワークがリスト、マップ、その他の構造と読みやすい印刷をサポートしています。
③:多用途性
最近のサービスには複数の言語やプラットフォームが含まれることが多く、クロスプラットフォームおよびクロス言語の相互通信をサポートできるかどうかがシリアル化選択の基本条件となります
④:互換性
最新のサービスは高速に反復され、アップグレードされます。優れたシリアル化フレームワークは、良好な前方互換性を備え、フィールドの追加、削除、変更などをサポートする必要があります。
⑤:拡張性
シリアル化フレームワークが低いしきい値のカスタム形式をサポートできるかどうかは、重要な考慮事項となる場合があります。
4. プーリング
その本質は、プールを作成することでオブジェクトの再利用を向上させ、作成と破棄を繰り返すオーバーヘッドを削減することです。
一般的なプーリング テクノロジには、メモリ プール、スレッド プール、接続プール、オブジェクト プールなどが含まれます。
1) メモリプール
C/C++ でメモリを割り当てるために malloc/free と new/delete がそれぞれ使用され、基礎となる呼び出しシステムが sbrk/brk を呼び出すことは誰もが知っています。メモリの割り当てや解放を行うシステムコールを頻繁に呼び出すと、パフォーマンスに影響を与えるだけでなく、メモリの断片化が起こりやすくなり、メモリプール技術はこれらの問題を解決することを目的としています。これらの理由から、C/C++ のメモリ操作はシステム コールを直接呼び出すのではなく、独自のメモリ管理セットを実装しています。
malloc には主に 3 つの実装があります。
①、ptmalloc: glibc の実装。
②、tcmalloc:Googleの実装。
③、jemalloc:Facebookの実装。
tcmalloc と jemalloc のパフォーマンスは似ていますが、ptmalloc のパフォーマンスは 2 つほど良くありません。Redis と mysql はどちらの malloc を使用するかを指定でき、ニーズに応じてより適切な malloc を選択できます。
3 つのレベルのメモリ管理:
2) スレッドプール
スレッド プールにより、アプリケーションは CPU、メモリ、ネットワーク、IO などのシステム リソースを最大限に活用できるようになります。作成されるスレッドの数を制限し、作成されたスレッドを再利用することで、システムのパフォーマンスが向上します。
スレッドを作成するには、仮想マシン スタック、ローカル メソッド スタック、プログラム カウンターなどのスレッド プライベート メモリ空間を開く必要があります。
これらのシステム リソースは、スレッドが破棄されたときに再利用する必要があります。したがって、スレッドの作成と破棄を頻繁に行うと、大量のシステム リソースが無駄になり、同時プログラミングのリスクが増加します。
また、サーバーが過負荷になったときに、新しいスレッドを待機させたり、フレンドリーなサービス拒否を実行したりするにはどうすればよいでしょうか? これらはスレッド自体では解決できないものです。そのため、スレッドプールを通じて複数のスレッドを調整し、プライマリスレッドとセカンダリスレッドの分離、タイミング実行、定期実行などのタスクを実装する必要があります。スレッド プールの機能は次のとおりです。
①: スレッドプールを使用して、スレッドの管理と再利用、最大同時実行数の制御などを行います。
②: タスクスレッドのキューキャッシュ戦略と拒否メカニズムを実装する
③: タイミング実行や定期実行などの時間関連機能を実現
④:スレッド環境の分離(分類またはグループ化)
グループ化: 2 つ以上のスレッド プールを構成することで、タスク間の相互影響を避けるために、遅いタスクを他のタスクから分離するなど、異なるタスクが異なるスレッド プールを使用します。
分類: コアと非コアに分類できます。コア スレッド プールは常に存在し、リサイクルされません。非コアは、システム リソースを節約するために、アイドル時間の経過後にスレッドをリサイクルすることがあります。必要に応じて、 、それらはオンデマンドで作成され、プールに配置されます。
3) 接続プール
一般的な接続プールには、データベース接続プール、Redis 接続プール、TCP 接続プールなどが含まれます。
その主な目的は、接続を多重化することで接続の作成と解放のオーバーヘッドを削減することです。接続プールの実装では通常、次の問題を考慮する必要があります。
①:初期化タイミング
スタートアップは初期化または遅延初期化であり、通常はスタートアップと初期化を使用します。
開始初期化により、一部のロック操作が軽減され、必要なときに直接使用できるようになりますが、サービスの起動が遅くなったり、起動後にタスクが処理されなくなったりして、リソースが無駄に消費される可能性があるという欠点があります。
遅延初期化とは、使用時に作成することです。この方法は、リソースの使用量を削減するのに役立ちますが、突然のタスク要求に直面して、瞬時に大量の接続を作成すると、システムの応答が遅くなったり、応答しなくなる可能性があります。
②:接続数
必要な接続数のバランスを調整します。接続が少なすぎるとタスクの処理が遅くなる可能性があり、接続が多すぎるとタスクの処理が遅くなるだけでなく、システム リソースを過度に消費します。
③:接続部分を取り出す
接続プールに使用可能な接続がない場合、使用可能な接続ができるまで待機するか、新しい一時接続を割り当てるか
④:接続を戻す
接続が使い果たされ、接続プールがいっぱいでない場合は、接続を接続プールに入れます (3 で作成した一時接続を含む)。それ以外の場合は、接続を閉じます。
⑤:接続有効性検出
長時間アイドル状態の接続や古い接続は閉じて、接続プールから削除する必要があります。一般的に使用される検出方法は、使用中の検出と定期的な検出です。
4) オブジェクトプール
厳密に言えば、すべての種類のプールはオブジェクト プール パターンを適用したものです。
上記の他のプールと同様に、オブジェクト プールは、次のようなインスタンスの数を制限しながら、同じタイプのオブジェクトが多数作成されることを避けるために一部のオブジェクトもキャッシュします。
①: Redis 内の 0 ~ 9999 の整数オブジェクトはオブジェクト プールを使用して共有されます
②: オブジェクトプールモードはゲーム開発でよく使われるモードで、例えばマップ上にモンスターやNPCが登場する際、毎回再作成されるのではなく、オブジェクトプールから取得されます。
③: mdm の RedisTemplate オブジェクト キャッシュ、uvcas の TalosProducer オブジェクト キャッシュ
public static Final Map<String, RedisTemplate<String, Object>> REDIS_TEMPLATE_MAP = new ConcurrentHashMap<>(); // 現在のスレッドのローカル変数 RdmContext rdmContext = RdmContext.currentContext(); RedisProperties redisProperties = rdmContext.getRedisProperties(); 文字列トークン = rdmContext.getToken(); // キャッシュされた redisTemplate があるかどうかを確認する RedisTemplate<String, Object> redisTemplate = RdmCache.REDIS_TEMPLATE_MAP.get(token); if (redisTemplate != null) { rdmContext.setRedisTemplate(redisTemplate); 戻る; } // RedisTEMplate とキャッシュを作成する // キャッシュする RdmCache.REDIS_TEMPLATE_MAP.put(トークン, redisTemplate);
5. 同時実行性
1) リクエストの同時実行性
タスクが複数のサブタスクを処理する必要がある場合、依存関係のないサブタスクを並列化できます。このシナリオはバックグラウンド開発では非常に一般的です。たとえば、リクエストで 3 つのデータをクエリする必要がある場合、それぞれ T1、T2、T3 の時間がかかります (シリアル呼び出しに時間がかかる場合、T=T1+T2+T3)。3 つのタスクを同時に実行すると、合計時間 T=max(T1,T 2,T3) がかかります。書き込み操作についても同様です。同種のリクエストについては一括マージも同時に実行できるため、RPCコール数が削減されます。
2) 冗長なリクエスト
冗長リクエストとは、複数の同一のリクエストをバックエンド サービスに同時に送信することを指し、すぐに応答した人が使用され、その他は破棄されます。この戦略はクライアントの待機時間を短縮しますが、システム全体の呼び出し量も増加します。一般に、初期化やリクエストがほとんどないシナリオに適用できます。
6. 非同期
処理に時間がかかるタスクの場合、同期待機方式を使用するとシステムのスループットが大幅に低下するため、非同期を使用することで解決できます。
1) 非同期呼び出し
時間のかかる RPC 呼び出しまたはタスク処理を行う場合、一般的に使用される非同期メソッドは次のとおりです。
①:コールバック
非同期コールバックは、コールバック関数を登録して非同期タスクを起動し、タスク実行時にユーザーが登録したコールバック関数をコールバックすることで呼び出し側の待ち時間を短縮します。この方法では、コードが分散して保守が困難になり、問題を特定するのが比較的困難になります。
②:未来
ユーザーがタスクを送信すると、即座に Future が返され、タスクは非同期に実行され、Future を通じて実行結果を取得できます。
// 非同期同時タスク Future<Response> f1 = Executor.submit(query1); // 他のことを処理する doSomething(); //結果を取得する 応答 res1 = f1.getResult();
③:CPS
(継続渡しスタイル) 複数の非同期プログラミングを配置してより複雑な非同期処理を形成し、同期コード呼び出しの形式で非同期効果を実現できます。
CPS は後続の処理ロジックをパラメータとして then に渡し、最終的に例外をキャッチできるため、散在する非同期コールバック コードと困難な例外追跡の問題が解決されます。
Java および C++ PPL の CompletableFuture は基本的にこの機能をサポートします。一般的な呼び出しは次のとおりです。
void handleRequest(const リクエスト &req) { return req.Read().Then([](バッファ&inbuf){ ハンドルデータ(inbuf)を返します; }).Then([](バッファ&outbuf){ return handleWrite(outbuf); })。ついに(){ cleanUp() を返します。 }); }
2) プロセスの非同期
ビジネス プロセスには長い呼び出しチェーンと多くの事後依存関係が伴うことが多く、これによりシステムの可用性と同時処理能力が同時に低下します。
MQ など、重要ではない依存関係の非同期解決を使用できます。
7. キャッシュ
シングルコア CPU から分散システムまで、フロントエンドからバックエンドまで、キャッシュはあらゆる場所に存在します。
キャッシュは元のデータのコピーセットであり、その本質は空間を時間と交換することであり、主に高い同時読み取りを解決するためにあります。
1) キャッシュの利用シーン
キャッシュは空間を時間と交換する技術であり、キャッシュを使用するとシステムのパフォーマンスを向上させることができます。
いわゆるパフォーマンス向上のためにコストを気にせずキャッシュを使用するのではなく、現場を見て注意してください。
①:一度生成すると基本的に変更されないデータ
②: 読み取り集中型またはホットスポットのあるデータ
③:高価なデータを計算する
④:同一データ
キャッシュに適さないシナリオ:
①: 書く量を増やし、読む量を減らし、頻繁に更新する
②: データの一貫性に関する厳しい要件
2) キャッシュの分類
①:プロセスレベルキャッシュ
キャッシュされたデータはプロセスのアドレス空間に直接存在するため、これがキャッシュにアクセスする最も速くて簡単な方法となります。
主な欠点は、プロセス空間のサイズによって制限され、キャッシュできるデータ量が制限されており、プロセスが再起動されるとキャッシュされたデータが失われることです。通常、JVM キャッシュなど、キャッシュされたデータの量が大きくないシナリオで使用されます。
②: 集中キャッシュ
キャッシュされたデータは、共有メモリなどの 1 台のマシンに集中されます。このタイプのキャッシュの容量は主にマシンのメモリのサイズによって制限され、プロセスの再起動後にデータが失われることはありません。一般的に使用される集中型キャッシュ ミドルウェアには、スタンドアロン バージョンの redis、memcache などが含まれます。
③:分散キャッシュ
キャッシュされたデータは複数のマシンに分散されており、通常、大量のキャッシュされたデータを各マシン ノードに均等に分散するには、データ シャーディングに特定のアルゴリズム (ハッシュなど) を使用する必要があります。一般的に使用されるコンポーネントは次のとおりです: Memcache (クライアントの断片化)、Codis (プロキシの断片化)、Redis Cluster (クラスターの断片化)
④:多層キャッシュ
アクセス効率を向上させ、バックエンドストレージへの影響を軽減するために、システム内のさまざまなレベルでデータをキャッシュすることを指します。
ローカルキャッシュ: カフェイン
外部キャッシュ: Redis
3) いくつかの優れたプラクティスをキャッシュする
①:静的分離と動的分離
キャッシュ オブジェクトの場合、多くの種類の属性に分割することができ、そのうちのいくつかは静的で、いくつかは動的です。キャッシュするときは動的と静的な分離を使用するのが最善です
②:大きなものの取り扱いには注意
キャッシュ オブジェクトが大きすぎる場合、特に Redis のようなシングル スレッド アーキテクチャでは、各読み取りと書き込みのオーバーヘッドが非常に高くなり、他のリクエストがブロックされる可能性があります。典型的な状況は、値フィールドに大量のリストをぶら下げたり、境界のないリストを格納したりすることですが、この場合、データ構造を再設計するか、値を分割してクライアントで集計する必要があります。
③:有効期限の設定
ダーティデータとストレージの使用量を減らすために有効期限を設定するようにしてください。ただし、有効期限が一定期間に集中しないように注意してください。
④:タイムアウト設定
データ アクセスを高速化する手段として、通常、キャッシュにはタイムアウト期間を設定する必要がありますが、タイムアウト期間は長すぎてはなりません (約 100 ミリ秒など)。そうしないと、リクエスト全体がタイムアウトになり、ソースに戻る機会がなくなります。
⑤:キャッシュ分離
まず第一に、競合や相互上書きを防ぐために、企業ごとに異なるキーが使用されます。次に、コア サービスと非コア サービスは、異なるキャッシュ インスタンスを通じて物理的に分離されます。
⑥:ダウングレード失敗
キャッシュを使用するには、特定のダウングレード プランが必要です。通常、特にコア サービスの場合、キャッシュは重要なロジックではありません。キャッシュが部分的に無効であるか失敗した場合は、リターンを直接中断するのではなく、ソースに戻り続ける必要があります。
⑦:容量制御
キャッシュの使用には、特にローカル キャッシュの容量制御が必要です。キャッシュが多すぎてメモリが不足している場合、ストレージ領域のスワップや GC 操作が頻繁に行われると、応答速度が低下します。
⑧:ビジネス志向
ビジネス志向であり、キャッシュのためのキャッシュは行わないでください。パフォーマンス要件が高くない、またはリクエスト量が大きくなく、分散キャッシュとデータベースさえも十分に対応できる場合、ローカル キャッシュを増やす必要はありません。そうでない場合は、データ ノード レプリケーションと冪等処理ロジックの導入が必要になる場合があります。ろうそくの価値はありません。
⑨:監視アラーム
大きなオブジェクト、遅いクエリ、メモリ使用量などを監視します。
8. 断片化
フラグメンテーションとは、大きな部分を複数の小さな部分に分割することですが、ここではデータシャーディングとタスクシャーディングに分けます。
データ シャーディングの場合、さまざまなシステムの分割に関する専門用語 (リージョン、シャード、vnode、パーティションなど) を総称してシャーディングと呼びます。
フラグメンテーションは、大規模なデータセットをより多くのノードに分散し、1 点の読み書き負荷も複数のノードに分散し、スケーラビリティと可用性を向上させる一石三鳥の技術と言えます。
データ シャーディングは、プログラミング言語の標準ライブラリのコレクションから分散ミドルウェアに至るまで、次のような遍在的です。
Java スレッドセーフな ConcurrentHashMap は、ロック競合を軽減するために、ハッシュまたはモジュロに従ってオブジェクトを特定のセグメントに配置するセグメンテーション メカニズムを採用しています。
分散メッセージミドルウェアKafkaでも、トピックは複数のパーティションに分割されており、各パーティションは互いに独立しており、同時に読み取りと書き込みが可能です。
1) 断片化戦略
シャーディングするときは、すべてのノードにデータを均等に分散して負荷を分散するようにしてください。
分散が不均一な場合、歪みが生じ、システム全体のパフォーマンスが低下します。一般的な断片化戦略は次のとおりです。
①:インターバルフラグメンテーション
連続キーワードに基づく断片化はソートを維持し、範囲検索に適しており、壊れた断片の読み取りと書き込みを減らします。インターバルシャーディングの欠点は、不均一なデータ分散とホットスポットが発生しやすいことです。たとえば、ID 範囲に基づいたシャーディング
また、時間範囲ごとにシャーディングすることも一般的であり、通常、最新の期間の読み取りおよび書き込み操作は、長い期間の読み取りおよび書き込み操作よりも頻繁に行われます。
②:ランダムシャーディング
このように特定の方法(ハッシュモジュロなど)に従ってデータが均等に分散されるため、ホットスポットや同時実行のボトルネックが発生しにくくなります。
欠点は、範囲クエリを実行するときに複数のノードへのリクエストが開始されるなど、秩序ある隣接関係の特性が失われることです。
③:組み合わせ
間隔シャーディングとランダム シャーディングの間の妥協点として、2 つの方法を組み合わせたものが採用されます。複合キーは複数のキーで構成され、最初のキーはランダム ハッシュに使用され、残りのキーは間隔ソートに使用されます。
WeChat モーメント、QQ Talk、Weibo などのソーシャル シナリオでは、ユーザー ID とリリース時間 (user_id、pub_time) の組み合わせを使用して、一定期間のユーザーのパブリケーション レコードを検索します。
2) 二次インデックス
セカンダリ インデックスは通常、特定の値の検索を高速化するために使用され、レコードを一意に識別することはできません。セカンダリ インデックスを使用するには 2 つの検索が必要です。
リレーショナル データベースと一部の KV データベースは、MySQL の非クラスター化インデックスなどのセカンダリ インデックスをサポートしており、ES 転置インデックスは用語を通じてドキュメントを検索します。
①:ローカルセカンダリインデックス
セカンダリ インデックスはキーと同じパーティションに保存されます。つまり、インデックスとレコードは同じパーティションにあります。
このように、すべての書き込み操作は 1 つのパーティション内で実行され、パーティション間の操作は必要ありません。ただし、読み取り操作の場合は、他のパーティション上のデータを集約する必要があります。
②:グローバルセカンダリインデックス
キーとは独立して、インデックス値自体によってパーティション化します。
このように、インデックスのデータの読み取りはすべて 1 つのパーティションで実行されますが、書き込み操作の場合は複数のパーティションにまたがる必要があります。
3) ルーティング戦略
ルーティング ポリシーは、シャード調整されたルートなど、指定されたノードにデータ リクエストを送信する方法を決定します。
通常、クライアント ルーティング、プロキシ ルーティング、クラスタ ルーティングの 3 つのルーティング戦略があります。
①:クライアントルーティング
クライアントはシャーディング ロジックを直接操作し、シャードとノードの割り当て関係を認識し、ターゲット ノードに直接接続します。
Memcache はこのように配布されます
②:プロキシ層ルーティング
クライアントのリクエストはプロキシ層に送信され、プロキシ層はそれを対応するデータ ノードに転送します。
Redis に基づく業界の分散ストレージ Codis など、多くの分散システムがこの方式を採用しています。
③:クラスタルーティング
フラグメント ルーティングはクラスターによって実装され、クライアントは任意のノードに接続します。ノードに要求されたフラグメントがある場合は処理されます。そうでない場合、要求は適切なノードに転送されるか、クライアントはターゲット ノードにリダイレクトされます。
たとえば、redis クラスターはこの方法を採用しています
上記の方法にはそれぞれ長所と短所があり、クライアント ルーティングの実装は比較的単純ですが、ビジネスの侵入に対しては比較的強いです。
プロキシ層のルーティングはサービスに対して透過的ですが、ネットワーク伝送の層が追加されます。これはパフォーマンスに一定の影響を及ぼしますが、導入は比較的複雑です。
クラスター ルーティングはビジネスに対して透過的であり、プロキシ ルーティングよりも構造が 1 つ少ないためコストが節約されますが、実装はより複雑であり、不合理な戦略により複数のネットワーク送信が増加します。
4) ダイナミックバランス
バイナリ ツリーと赤黒ツリーのバランスを学ぶとき、データの挿入と削除によってバランスが崩れることは誰もが知っています。ツリーのバランスを維持するために、挿入と削除後に、ツリーの高さを左手と右手で動的に調整し、再バランスを維持します。分散データ ストレージも再バランスする必要がありますが、主に次の側面で不均衡を引き起こす要因は他にもあります。
①: 読み書き負荷が増加し、より多くの CPU が必要になります
②:データ規模が増大し、より多くのディスクとメモリが必要になる
③: データノードに障害が発生し、他のノードに置き換える必要がある
業界には、Redis クラスターのリシャーディング、HDFS/kafka のリバランスなど、動的なバランス調整をサポートする製品が多数あります。一般的な方法は次のとおりです。
①:固定パーティション
ノード数をはるかに超える数のパーティションを作成し、各ポイントに複数のパーティションを割り当てます。
新しいノードが追加された場合は、バランスを保つために既存のノードから複数のパーティションを均等に削除できます。また、ノードを削除する場合はその逆を行うことができます。
②:動的パーティション
パーティション数を自動的に増減し、パーティションデータが一定のしきい値を超えると分割されます。パーティション データが特定のしきい値まで縮小すると、パーティションはマージされます。
B+ ツリーの分割削除操作と同様です。HBase リージョンの分割と結合、TDSQL の Set Shard など、多くのストレージ コンポーネントがこの方式を採用しています。
この方法の利点は、データ量に自動的に適応し、優れた拡張性があることです。このタイプのパーティションを使用する場合の注意点は、初期化されたパーティションが 1 つだけの場合、オンラインになった直後に大量のリクエストがあった場合に単一点の負荷が高くなるということです。通常、複数のパーティションが事前に初期化されます。この問題を解決するには、HBase の事前分割などを使用します。
5) サブデータベースとサブテーブル
データベースに大量の単一テーブル/スタンドアロン データがある場合、パフォーマンスのボトルネックが発生します。データベースへの負荷を分散し、読み取りおよび書き込みのパフォーマンスを向上させるには、分割統治を採用する必要があります。データベースとテーブルを分割する戦略。通常、サブデータベースのサブテーブルは次の状況で必要になります。
①: 1つのテーブルのデータ量が一定レベル(mysqlなど一般的には数千万)に達すると、読み書きのパフォーマンスが低下します。現時点では、インデックスが非常に大きくなり、パフォーマンスが低下し、単一のテーブルを分解する必要があります。
②: データベースのスループットがボトルネックに達し、データの読み取りと書き込みの負荷を分散するためにデータベース インスタンスを追加する必要がある
サブデータベースおよびサブテーブルは、特定の条件に従ってデータを複数のデータベースおよびテーブルに分散します。これは、垂直分割と水平分割の 2 つのモードに分けることができます。
①:縦分割
ビジネスやモジュールの種類などの特定のルールに従って、1 つのデータベース内の複数のテーブルを異なるデータベースに分散します。
アドバンテージ:
①:セグメンテーションルールが明確で事業区分が明確である
②:業務の種類や重要度に応じたコスト管理ができ、拡張も便利
③:簡単なデータメンテナンス
欠点:
①:異なるテーブルが異なるライブラリに割り当てられており、結合によるテーブルの接続はできません。しかし、実際の業務設計では基本的に結合操作は行わず、マッピングテーブルを構築し、2回のクエリや書き込み時によりパフォーマンスの高いストレージシステムにデータを構築して格納することが一般的です。
②:トランザクション処理が複雑になり、トランザクション内で元々動作していた同一ライブラリの異なるテーブルがサポートされなくなりました。現時点では、フレキシブル トランザクションまたはその他の分散トランザクション スキームを使用できます。
②:水平分割
ハッシュやモジュロなどの特定のルールに従って、同じテーブル内のデータを複数のデータベースに分割します。
単純に行単位での分割と理解でき、分割後のテーブル構造は同じです。
アドバンテージ:
①:分割後もテーブル構造は同じで業務コードの変更は不要
②: 単一テーブル内のデータ量を制御できるため、パフォーマンスの向上につながります
欠点:
①: 結合、カウント、レコードのマージ、ソート、ページングなどの問題をノード間で処理する必要がある
②: 比較的複雑なルーティング戦略を実装する必要があります。
まとめると、垂直分割と水平分割にはそれぞれ長所と短所があり、通常はこれら 2 つのモードを組み合わせて使用します。
6) タスクのシャーディング
タスク シャーディングでは、タスクを複数のサブタスクに分割して並列処理し、タスクの実行を高速化します。これには通常、データ シャーディングが含まれます。たとえば、マージ ソートでは、最初にデータが複数のサブシーケンスに分割され、最初に各サブシーケンスが並べ替えられ、最後に順序付けされたシーケンスが合成されます。
ビッグ データ処理では、Map/Reduce はデータ シャーディングとタスク シャーディングの古典的な組み合わせです。
9. 保管
1) 読み取りと書き込みの分離
ほとんどのビジネスでは読み取りが増え、書き込みが減ります。システムの処理能力を向上させるために (書き込み時にロックされて読み取れないため)、マスター ノードを書き込みに使用し、スレーブ ノードを読み取りに使用できます。
読み取り/書き込み分離アーキテクチャには次の特徴があります。
①: データベース サービスのマスター/スレーブ アーキテクチャ。1 つのマスターと 1 つのスレーブ、または 1 つのマスターと複数のスレーブにすることができます。
②: マスターノードが書き込み操作を担当し、スレーブノードが読み取り操作を担当します。
③: マスターノードはデータをスレーブノードにコピーします; 基本アーキテクチャに基づいて、マスター-マスター-スレーブ、マスター-スレーブ-スレーブなど、さまざまな読み書き分離アーキテクチャを変更できます。マスターノードとスレーブノードは、mysql+redis などの異なるストレージにすることもできます。
読み書き分離したマスター/スレーブアーキテクチャは一般に非同期レプリケーションを採用しており、データレプリケーションの遅延が問題となるため、高いデータ一貫性を必要としないビジネスに適しています。レプリケーションの遅延によって引き起こされる問題を回避するには、次の方法を使用できます。
①: 読み取り後書き込みの一貫性
つまり、自分の書き込みを読み取ります。これは、書き込み操作後にリアルタイムの更新が必要なユーザーに適しています。典型的なシナリオは、ユーザーがアカウントの登録またはアカウントのパスワードを変更した後にログインする場合ですが、このとき、スレーブノードに読み取りリクエストが送信されると、データがまだ同期されていない可能性があるため、ユーザーはログインに失敗します。それは容認できないことです。この場合、独自の読み取りリクエストをマスター ノードに送信できますが、他のユーザー情報を表示するリクエストは引き続きスレーブ ノードに送信されます。
②:二次読み物
最初にスレーブ ノードから読み取ります。読み取りが失敗するか、追跡更新時間が特定のしきい値未満の場合は、マスター ノードから読み取ります。
③: 基幹業務の読み書きマスターノード、非基幹業務の読み書き分離
④:単調読み
ロールバックを避けるために、すべてのユーザー読み取りリクエストが同じスレーブ ノードに送信されるようにしてください。たとえば、ユーザーが M マスター ノードの情報を更新すると、データはすぐにスレーブ ノード S1 に同期され、ユーザーが問い合わせるとリクエストが S1 に送信され、更新された情報が表示されます。このとき、データの同期が完了していないスレーブノード S2 にリクエストが送信され、更新された情報が消えてしまった、つまりデータがロールバックされたという現象がユーザーに表示されます。
2) 静的と動的の分離
動的および静的分離により、頻繁に更新されるデータと更新頻度の低いデータが分離されます。CDN で最も一般的なのは、Web ページは通常、静的リソース (picture/js/css など) と動的リソース (JSP、PHP など) に分割され、動的リソースと静的リソースを分離することによって静的リソースが CDN エッジ ノードにキャッシュされることです。動的リソースを要求するだけで済みます。それだけで、ネットワーク伝送とサービス負荷が軽減されます。
動的分離はデータベースと KV ストレージにも採用できます。
①:キャッシュでは静的フィールドと動的フィールドをキャッシュオブジェクトに分けてキャッシュします。
②: データベースでは、動的フィールドと静的フィールドの分離は垂直セグメンテーションに似ており、動的フィールドと静的フィールドを異なるデータベース テーブルに格納し、データベース ロックの粒度を減らし、同時に異なるデータベース リソースを割り当てることで使用率を合理的に向上させることができます。レート
3) 温冷分離
ホットとコールドの分離は、あらゆるストレージ製品や大規模ビジネスにとって不可欠な機能であると言えます。Mysql、ElasticSearch、CMEM、Grocery などはすべて、直接的または間接的にホットとコールドの分離をサポートしています。
ホット データをよりパフォーマンスの高いストレージ デバイスに配置し、コールド データを安価なディスクにシンクすることでコストを節約します。
たとえば、7 日間のホット データを保持すると、7 日を超えるデータ タスクはコールド データとして移行されます。
4) リライトライトリード
①: クリティカルな書き込み。非同期レプリケーションなどの読み取りの重要性を軽減します。マスター ノードが正常に書き込み、スレーブ ノードの読み取りが同期遅延を許容できることを確認するだけです。
②: ロジックをより多く書き、ロジックの読み取りを減らし、計算のロジックを読み取りから書き込みに移行します。共通リーダーボードがリクエストの読み取り時ではなく書き込み時に構築されるなど、読み取りリクエスト中に計算を実行する必要があるシナリオに適用できます。
5) データの異質性
データの異質性は主に、クエリを高速化するためにさまざまな次元に従ってインデックス関係を確立することです。
たとえば、JD.com や Tmall などのオンライン ショッピング モールは、通常、注文番号に応じてデータベースとテーブルを分割します。注文番号は同じテーブルにないため、購入者または販売者の注文リストをクエリするには、すべてのサブデータベースにクエリを実行し、データ集計を実行する必要があります。
異種インデックスを構築し、注文生成と同時に購入者と販売者が注文するためのインデックステーブルを作成することが可能で、このテーブルはユーザーIDに応じてデータベースとテーブルに分割できます。
10. 待ち行列
システム アプリケーションでは、すべてのタスクとリクエストをリアルタイムで処理する必要はありません。多くの場合、データには強い一貫性は必要ありませんが、最終的な一貫性を維持することだけが必要です。システム モジュール間の依存関係を知る必要がない場合もあります。これらのシナリオでは、データに強い一貫性は必要ありません。 、キューテクノロジーの約束
1) 応用シナリオ
①:非同期処理
通常、業務リクエストには多くの処理プロセスがあり、このリクエストではすぐに処理する必要のないプロセスもあり、このとき非同期処理を使用できます。
②:フローピーククリッピング
高同時実行性システムのパフォーマンスのボトルネックは、通常、データベースの読み取りおよび書き込みなどの I/O 操作にあります。突然のトラフィックに直面した場合は、メッセージ キューをキューイングとバッファリングに使用できます。
③:システムデカップリング
システム間の強調関係を解決し、ブロック通話を削除し、非同期メッセージに変更します。
④:データ同期
メッセージ キューは、特にシステム間でデータを同期する場合に、データ バスとして機能します。たとえば、RabbitMQ を介して、Mysql の書き込み時にデータが Redis に同期され、結果的に整合性のある分散キャッシュが実現されます。
⑤:柔軟な対応
従来の分散トランザクションは、2 フェーズ プロトコルまたはその最適化されたバリアントを使用して実装されます。トランザクションの実行時には、ロック リソースを競合して待機する必要があります。同時実行性の高いシナリオでは、システムのパフォーマンスとスループットが大幅に低下します。デッドロックさえ発生します。
インターネットの中核は高い同時実行性と高可用性であり、一般に従来のトランザクションの問題を柔軟なトランザクションに変換します。
フレキシブル トランザクションの中心的なプロセスは次のとおりです。
1): 分散トランザクション イニシエーターは、最初のローカル トランザクションを実行する前にトランザクション メッセージを MQ に送信し、MQ サーバーに保存します。MQ コンシューマーはメッセージを認識して消費することができません①②
2): トランザクションメッセージの送信に成功したら、スタンドアロントランザクション操作を開始します③
a. ローカルトランザクションが正常に実行された場合、MQサーバーのトランザクションメッセージを通常状態に更新します④
b. ダウンタイムやネットワークの問題によりローカル トランザクションが時間内に MQ サーバーにフィードバックされなかった場合、以前のトランザクション メッセージは常に MQ に保存されます。MQ サーバーはトランザクション メッセージを定期的にスキャンし、メッセージの保存時間が一定のしきい値を超えると、MQ 運用端末にトランザクションの実行状況を確認するリクエストを送信します⑤
c. ローカルトランザクション結果⑥を確認した後、トランザクションが正常に実行された場合は、以前に保存されたトランザクションメッセージを通常の状態に更新し、そうでない場合は、MQ サーバーに破棄するよう通知します。
3): コンシューマはトランザクション メッセージを取得し、通常の状態に設定した後、2 番目のローカル トランザクション ⑧ を実行します。実行が失敗した場合は、MQ 送信者にロールバックするか、最初のローカル トランザクションを積極的に補償するように通知します。
2) 用途分類
①:バッファキュー
キューの基本的な機能は、TCP の送信バッファなどのバッファリングとキューイングであり、アプリケーション層のバッファは通常ネットワーク フレームワークに追加されます。バッファキューを使用してバーストトラフィックを処理すると、処理がよりスムーズになり、システムが保護されます。
ビッグ データ ログ システムでは、通常、ログ収集システムとログ解析システムの間にログ バッファ キューを追加して、解析システムがブロックされたり、解析システムの負荷が低いときにログが破棄されたりするのを防ぐ必要があります。負荷を軽減し、独自のアップグレードとメンテナンスを容易にします。たとえば、データ収集システムでは、Kafka がログ バッファ キューとして使用されます。
②:リクエストキュー
ユーザーのリクエストをキューに入れます。ネットワーク フレームワークには一般にリクエスト キューがあります。たとえば、spp にはプロキシ プロセスとワーク プロセスの間に共有メモリ キューがあり、taf にもネットワーク スレッドとサーヴァント スレッドの間にキューがあり、主にフローに使用されます。制御、過負荷保護、タイムアウト破棄など。
③:タスクキュー
非同期実行のためにタスクをキューに送信します。最も一般的なのはスレッド プールのタスク キューです。
④:メッセージキュー
メッセージ配信には主にポイントツーポイントとパブリッシュサブスクライブの 2 つのモードがあり、一般的なものには RabbitMQ、RocketMQ、Kafka などがあります。
3.高可用性:
可用性: システムが利用可能な動作状態にある時間の割合を指します。
高可用性: システムを 100% に近い高可用性にする
具体的な指標:
MTBF (平均故障間隔): 平均故障間隔、平均故障間隔、つまりシステムが利用可能な時間の長さ (時間単位)
MTTR (平均修復時間): システムが障害から回復するまでにかかる時間
SLA (サービス レベル アグリーメント): サービス レベル アグリーメント。サービス可用性レベルを評価するために使用されます。計算式はMTBF/(MTBF+MTTR)です。
可用性は 99.99% (フォーナイン) より高いとよく言われますが、これは指標 SLA が 99.99% より高いことを意味します。
可用性 | 年間ダウンタイム | 毎日の故障時間 |
90% (9 件中 1 件) | 36.5日 | 2.4時間 |
99% (2 ナイン) | 3.65日 | 14.4分 |
99.9% (スリーナイン) | 0.365日、8時間 | 1.44分 |
99.99% (フォーナイン) | 0.0365日52分 | 8.6秒 |
99.999% (ファイブナイン) | 0.00365日、5分 | 0.86秒 |
技術的なアーキテクチャ、高可用性のための戦略は何ですか:
マルチクラウド アーキテクチャ、オフサイト マルチアクティブ、オフサイト バックアップ
アクティブ/スタンバイ切り替え: Redis キャッシュや MySQL データベースなど、アクティブ ノードとスタンバイ ノードがリアルタイムでデータを同期し、バックアップします。プライマリ ノードが使用できない場合は、自動的にスタンバイ ノードに切り替わります。
マイクロサービス、ステートレス アーキテクチャ、ビジネス クラスターの展開、ハートビート検出により、利用できないサービスを最短時間で検出できます
流量過負荷の問題を解決し、ヒューズと電流制限により過負荷保護を提供します。
Web セキュリティに注意し、攻撃や XSS の問題を解決する
1. システム分割
従来のシステムは、EC事業、会員、商品、受注、物流、マーケティングなどのモジュールが一つのシステムに積み上げられており、すべて単一システムでした。休日ごとに大きなプロモーションイベントが開催され、システムが拡張されると、1つの拡張が完全に拡張され、1つの拡張がすべてに吊り下げられます。1 つのインターフェイスに問題がある限り、システム全体が使用できなくなります。
したがって、巨大な単一システムに直面して、それをマイクロサービス アーキテクチャに分割する必要があります。DDD (Domain-DrivenDesign) の考え方に従って、複雑なビジネスをいくつかのサブシステムに分割し、各サブシステムが専用のビジネス機能を担当し、垂直構造がしっかりと行われ、各サブシステム間の境界分離が行われます。 . リスクの伝染を軽減します。
2. デカップリング
ソフトウェア開発には「高凝集性、低結合性」という重要な原則があります。
インターフェイスの抽象化や MVC 階層化から SOLID 原則に至るまで、23 の設計パターンがあります。重要なのは、誤った変更がシステム全体に影響を与えるのを避けるために、異なるモジュール間の結合を減らすことです。
Spring AOP、イベント駆動モデルなどのアイデア
3. 非同期
同期とは、スレッドがリクエストを実行しているときに、リクエストが情報を返すまでに時間がかかる場合、スレッドはブロックされ、戻り情報を受信するまで待機してから実行を続行することを意味します。
非リアルタイム応答アクションの場合、非同期で完了でき、スレッドは常に待機する必要はなく、背後のロジックを実行し続けます。
例: スレッド プール、メッセージ キュー
例: 注文の際に気になるのは、注文が正常に作成されたかどうか、その後の支払いプロセスが実行できるかどうかです。
SMS 通知、電子メール通知、注文スナップショットの生成、時間外のタスクのキャンセルなどの他のビジネス アクションについては、ユーザーはこれらの非コア アクションについてはあまり心配していませんが、メッセージ キューを使用してこれらの操作を非同期に実行できます。注文がデータベースに正常に挿入されたら、メッセージを MQ に送信し、ユーザーに成功を返します。すると、メッセージをリッスンするスレッドがこれらの操作を完了します。
4.再試行
再試行は主にリモート RPC 呼び出しに反映され、ネットワーク ジッターやスレッド リソースのブロックなどの要因の影響を受け、要求にタイムリーに応答できなくなります。
ユーザー エクスペリエンスを向上させるために、呼び出し元は結果の取得を再試行することでリクエストを再度送信できます。
インターフェイスの再試行は諸刃の剣であり、クライアントは応答タイムアウト結果を受け取りますが、サーバーが実行を完了したかどうかはわかりません。やみくもに再試行すると、重大な結果が生じる可能性があります。例: 銀行振込。
Retry は通常 idempotence と組み合わせて使用されます。インターフェイスが idempotence をサポートしている場合は、自由に再試行できます。
①:シーシュポス
②:スプリングリトライ
冪等解:
①:挿入前にクエリ操作を実行し、存在するかどうかを確認し、挿入するかどうかを決定します。
②:ユニークインデックスを追加します。
③: 対重時計を構築します。
④:決済後、注文ステータスを決済済みに調整し、SQL更新レコードの前に条件判定を追加するなどのステートマシンを導入します。
⑥:分散ロックを追加します。
⑦: トークンメカニズムが採用され、サーバーはトークン検証を追加し、最初のリクエストのみが正当です
5. 補償
補償を通じて、データの最終的な整合性を実現します
注: 補償操作には重要な前提条件があります。企業は短期間でデータの不整合を許容できるということです。
ビジネス補償は、処理の方向に応じて 2 つの部分に分かれています。
①:前進
部分的に成功した場合、複数の操作が分散トランザクションを構成します。部分的に失敗した場合、ベスト エフォート メカニズムを通じて失敗したタスクを成功状態にプッシュします。
②:リバース
操作を逆に行うことで、部分的に成功したタスクを初期状態に復元します
補償の実施方法:
①: テーブルをローカルに作成し、関連するデータを保存し、スケジュールされたタスクを通じてそれらをスキャンして抽出し、リフレクション メカニズムを利用して実行をトリガーします。
②: 単純なメッセージ ミドルウェアを使用して、下流の消費タスクによって実行されるビジネス メッセージ本文を構築することもできます。失敗した場合は、MQ の再試行メカニズムを使用して複数回再試行できます。
6. フェイルオーバー
フェイルオーバー: 通常、アクティブとスタンバイの切り替えを指し、障害時間を短縮します。
システム障害が発生した場合、最初の作業は原因をすぐに見つけることではありません。障害の複雑さを考慮すると、問題の特定とトラブルシューティングには時間がかかります。問題が解決された後、SLA は数回低下します。より良い解決策は、フェイルオーバーです。
フェイルオーバー: 障害のあるノードが見つかると、それを修復しようとするのではなく、そのノードはただちに隔離され、トラフィックは正常なノードに転送されます。このように、フェイルオーバーは MTTR を削減し、SLA を改善するだけでなく、障害が発生したノードを修復するための十分な時間を稼ぐこともできます。
①: ピアノードは直接転送および切り替えが可能
②:ノードがアクティブとスタンバイに分かれている場合、転送中にアクティブとスタンバイの切り替えが必要
障害を検知して自動転送する方法は?
一般に、ハートビート機構などの何らかの障害検出機構が採用されており、バックアップノードは定期的にハートビートパケットを送信しますが、ほとんどのノードがプライマリノードからハートビートパケットを受信しない場合は、プライマリノードに障害が発生していることを意味します。切り替えられる。
どのスタンバイ ノードに切り替えるか?
一般に、複数のバックアップ ノードの中から新しいマスター ノードを選択するには、paxos や raft などの分散コンセンサス アルゴリズムが使用されます。
アクティブ/スタンバイの切り替えは、大きく次の 3 つのステップに分かれます。
1) 自動障害検出 (auto-detect)。ヘルスチェック、ハートビートなどの手段を使用して、障害のあるノードを自動的に検出します。
2) 自動転送(フェイルオーバー) 障害ノードを検出した場合、トラフィックの削除やクラスタからの離脱などにより障害ノードを隔離し、正常なノードにトラフィックを転送します。
3) 自動復旧 (フェイルバック)。障害が発生したノードが正常に戻ると、そのノードが自動的にクラスターに追加され、クラスターのリソースが障害発生前と一貫性があることが保証されます。
7. マルチアクティブ戦略
災害復旧バックアップ戦略は、すべてがうまくいくことを保証するものではありません
コンピューター室の停電、コンピューター室の火災、地震、山岳地帯の急流、その他の不可抗力要因などの極端な状況では、すべてのサーバー (メインおよびバックアップ) が同時に障害を起こし、すべてのサーバーが外部サービスを提供できなくなる可能性があります。その結果、ビジネス全体が麻痺してしまいます。
リスクを軽減し、24 時間サービスを確実に利用できるようにするために、マルチアクティブ戦略を採用できます。
一般的なマルチアクティブ プログラムには、同じ都市でのマルチ アクティブ、2 か所で 3 つのセンター、3 か所で 5 つのセンター、異なる場所でのダブル アクティブ、異なる場所でのマルチ アクティブなどが含まれます。
8. 隔離
分離は物理レベルの部門に属し、いくつかの低結合システムを物理的に分離し、独立して展開します。
各サブシステムには独自の独立したコード ベースがあり、独立して開発およびリリースされます。障害が発生した場合でも、それらは相互に干渉しません。もちろん、異なるサブシステム間に相互依存性がある場合、この状況は非常に特殊であり、デフォルト値または例外の特別な処理が必要になります。これはビジネス レベルのソリューションです。
分離は、最も一般的なマイクロサービス ソリューションである分散テクノロジーから派生したものです。
大規模で複雑なシステムを複数のマイクロサービス システムに分割します。これらのマイクロサービス サブシステムは通常、異なるチームによって開発および保守され、独立してデプロイされ、サービスは RPC を通じてリモートで呼び出されます。
分離によりシステム間の境界が明確になり、障害がより分離され、問題の発見と解決がより速くなり、システムの可用性が高まります。
9. 電流制限、過負荷保護を提供
同時実行性の高いシステムで、現在のシステムの処理能力を超えるトラフィックのピークが発生した場合はどうすればよいでしょうか?
解決策としては、すべてのオーダーを受け付けてしまうと、CPU、メモリ、負荷が高騰し、最終的には処理できなくなり、すべてのリクエストがタイムアウトして正常に応答できなくなります。
別の解決策は、過剰なトラフィックを破棄することです
電流制限の定義:
システムに到達する同時リクエストの数を制限して、システムが一部のユーザー リクエストに正常に応答できるようにします。また、制限を超えるトラフィックについては、システム全体の可用性がサービス拒否によって保証されます。
電流制限の原理はサーキットブレーカーの原理に似ており、特定の条件を判断して特定の戦略を実行するかどうかを決定します。ただし、違いがあります。ヒューズが過負荷保護をトリガーし、ノードは復旧するまでサービスを一時停止します。現在の制限は、自分の能力の範囲内でのみリクエストを処理することであり、超過したリクエストは制限されます。
動作範囲に応じて、電流制限はスタンドアロン版電流制限、分散型電流制限に分けられます。
1. スタンドアロン版の電流制限
AtomicLong#incrementAndGet() などを使用してカウンタを実装するには主にローカル メモリを使用しますが、以前に使用されていなかったキーを定期的に消去してメモリを解放することに注意してください。
純粋なメモリの実装により、他のノードと統計を集計する必要がなく、パフォーマンスが最高になります。しかし、メリットにはデメリットもあり、世界的に統一した電流制限を実現することは不可能です。
2. 分布型電流制限
スタンドアロン版の電流制限は自身のノードのみを保護できますが、アプリケーションが依存するさまざまなサービスを保護することはできず、ノードの拡張または縮小時にサービス全体のリクエスト制限を正確に制御できません。
分散型電流制限では、クラスターをディメンションとして使用し、このクラスターのリクエスト制限を簡単に制御できるため、ダウンストリームが依存するさまざまなサービス リソースを保護できます。
電流制限によってサポートされる複数の次元:
①:一定時間内(1分あたりなど)にシステム全体で処理されるリクエストの数
②:一定時間内に単一インターフェースでどれだけのトラフィックを処理できるか
③:一定期間内に単一のIP、都市、チャネル、デバイスID、ユーザーIDなどから送信されたリクエストの数
④:オープンプラットフォームの場合、アプリキーごとに独立したアクセスレートルールを設定する
電流制限アルゴリズムには主に次のものがあります。
カウンタ電流制限、スライディング ウィンドウ電流制限、トークン バケット電流制限、リーキー バケット電流制限
10.ヒューズ、過負荷保護を提供します
いわゆる過負荷保護とは、荷重がシステムの耐荷重を超えると、システムが自動的に荷重が潰れないように保護措置を講じることを意味します。
実際、Fuse は、呼び出しリンク内のリソースが不安定な状態 (呼び出しのタイムアウトや比率の異常な増加など) にある場合に、このリソースの呼び出しを制限することで、リクエストがすぐに失敗し、他のリソースに影響を与えてオンライン状態が引き起こされるのを回避します。間違い。
例: サーキット ブレーカーのトリガー条件は、システム ノードの処理能力とサービス品質に関連することがよくあります。たとえば、CPU 使用率が 90% を超え、リクエスト エラー率が 5% を超え、リクエスト遅延が 500ms を超えた場合、そのうちの 1 つが条件を満たし、サーキット ブレーカーが発生します。
融合の主な方法は、サーキット ブレーカーを使用して、障害が発生したサーバーの呼び出しをブロックすることです。
サーキットブレーカーには、閉、開、半開の 3 つの状態があります。
①:クローズド(Closed)状態: この状態では、リクエストはバックエンドサービスに転送されます。同時にリクエスト失敗回数も記録され、一定時間リクエスト失敗回数が一定回数を超えるとオープン状態になります。
②: オープン (Open) 状態: この状態では、ヒューズはバックエンド サービスを呼び出すことなく、リクエストを直接拒否し、エラーを返します。同時にタイマーがかかり、時間が経過すると半開きになります。一定期間が経過すればサービスが通常に戻ることを想定しています。
③: ハーフオープン状態: この状態では、サーキット ブレーカーは、バックエンド サービスが復元されたかどうかを検出するために、いくつかのリクエストをバックエンド サービスに転送しようとします。リクエストが失敗するとオープン状態、成功するとクローズ状態となり、同時にカウントがリセットされます
11. ダウングレード
ダウングレードはシステムを保護する重要な手段です
限られたリソースの価値を最大化するために、システムの負荷を軽減し、限られたリソースをコア業務に確保するために、一部の非コア機能を一時的に停止します。
たとえば、電子商取引のプロモーション中、ビジネスのピーク時にシステムがすべてのトラフィックに耐えられず、システム負荷と CPU 使用率が警告レベルを超え、システム負荷を軽減するために一部の非コア機能がダウングレードされることがあります。商品の評価や取引記録などの機能が一時的に停止されます。ハンサムを保護し、注文の作成や支払いなどのコア機能を正常に使用できるようにするために車を放棄します。
要約すると、ダウングレードとは、一部の非コア サービスまたはコンポーネントを一時的にシャットダウンすることで、コア システムの可用性を保護することです。
12. タイムアウト制御
分散環境では、サービスの応答が遅いことはダウンタイムよりも有害である可能性があります。障害は一時的なものですが、呼び出しの遅延により占有されているリソースが解放されず、同時実行性が高いとシステム全体がクラッシュします。
タイムアウト期間を適切に設定するにはどうすればよいですか?
システム間の通話ログを収集し、たとえば 99% の応答時間がどれくらいかをカウントし、この時間に基づいてタイムアウト期間を指定します。
タイムアウト処理戦略?
①:タイムアウト後にサービスがリソースを解放し、応答が失敗する
データベースにタイムアウト期間が設定されている場合、タイムアウトが経過すると操作が終了し、リソースが解放されます。
jdbc設定:
connectTimeout: MySQL とのソケット接続の確立を待機するタイムアウト時間を示します。デフォルト値は 0 で、タイムアウトなしを意味します。単位はミリ秒です。30000 を推奨します。
socketTimeout: クライアントと MySQL がソケットを確立した後のソケットの読み取りおよび書き込みの待機タイムアウト時間を示します。
②: ネットワークの変動やノードの異常によるリクエストのタイムアウトに対して、サービスの低下を利用してリクエストに対する包括的なデータ応答を提供し、ユーザー インターフェイスの長時間の停止を回避できます。
高可用性設計理論:
CAP: 一貫性、可用性、パーティション耐性。この理論はよく知られており、最終的には CP と AP の間で比較検討され、BASE (基本的に利用可能、ソフト状態、最終的には一貫性) を満たすバランスの取れた結果が見つかります。
高可用性設計要素:
冗長性: システムの動作に重要な要素に、障害が発生した場合に引き継ぐことができる追加の冗長コンポーネントがあることを保証します。
監視: 実行中のシステムからデータを収集し、コンポーネントに障害が発生したり応答が停止したりしたことを検出します。
フェイルオーバー: 手動または自動のメカニズム。監視によりアクティブなコンポーネントに障害が発生したことが示された場合、メカニズムは現在アクティブなコンポーネントから冗長コンポーネントに切り替えることができます。
上記の 3 つの要素のロジックも非常に明確です。高可用性を実現するには、状態の有無に関係なく、冗長性またはバックアップが必要です。障害が発生した場合は、障害を監視する監視手段が必要です。コンポーネントは迅速に動作します。以前は冗長だったコンポーネントに転送され、中断のないサービスが保証されます。
高可用性ソリューションの設計について、どのような角度から議論し、考えるべきでしょうか?
まず、アプリケーション側、サポート側、運用保守側では設計手法が異なります。
アプリケーション側の高可用性は、前述の冗長性、クラスタリング、ロードバランシングなどによる迅速なフェイルオーバーを実現するだけでなく、ヒューズ、電流制限、フォールトトレランス、ダウングレード、緊急時などの安全対策、タイムアウトおよび再試行戦略も含みます。フレームワーク コンポーネント、非同期呼び出し、冪等設計を補うために。
サポート側 (またはインフラストラクチャ プラットフォーム) には、早期警告、迅速な警報、視覚的な監視、障害の分析に対応するために、高可用性関連の監視指標の完全なセットが必要です。一般的な指標には、リクエスト量、リクエストエラー率、平均遅延、HTTP ステータス、およびシステムリソース消費に関連する指標が含まれます。
運用および保守側の重要なポイントは DevOps であり、自動リリース、グレースケール リリース、エレガント リリース、バージョン管理、ヘルス チェックなどの機能により、ビジネス障害が発生する前および発生した際に、アプリケーションがサービスを利用できない状態を最小限に抑えることができます。