XBTトラッカーを使用して1秒間に数千のクエリを処理する

テストが最近行われ、その結果、1つのアプリケーションが負荷が小さいだけでなく、ささやかなサーバーで1秒あたり2000リクエストを処理することが示されました。 この場合、各クエリの結果はMySQLの3〜5個のテーブルに記録されます。 正直なところ、この結果は私を驚かせたので、このアプリケーションのアーキテクチャの説明をhabrasocietyと共有することにしました。 同様のアプローチは、バナー表示からチャットやマイクロブログに適用できます。誰かがそれを面白いと思うことを願っています。



まず、このアプリケーションはシングルスレッドです。 すべてが1つのプロセスで行われ、ソケットで動作します-非ブロッキングepoll / select、入力/出力を待機するスレッド(スレッド)はありません。 HTTPの開発、最初のKeep-Aliveの出現、次にAJAXとCOMETの人気の高まりにより、Webサーバーへの永続的な接続の数が増加し、ロードされたプロジェクトで数千から数万の単位で測定され、それぞれ独自のスタックで独自のスレッドを作成し、それらを絶えず切り替えます-サーバーのリソースはすぐに十分ではありません。



2番目の重要な点は、1つのSELECT ... WHERE pk in(k1、k2、...、kN)が複数のSELECT ... WHERE pk = ...よりも高速であるということです。データベースを大量に処理することで、1秒あたりのクエリ数を減らすことができます。そして全体的な負荷。



サブジェクトエリア



XBTトラッカー(XBTT)-bittorrentトラッカー。 トレントは、たとえばWorld of WarcraftのLinuxディストリビューションやパッチを配布するために公式に使用されているため、著作権のトピックはご遠慮ください。 ed2kやDC ++とは異なり、アーカイブにパックせずに複数のファイルを1つのトレントに入れ、いつでもファイルの整合性をチェックし、必要に応じて壊れたピースをダウンロードして復元することができます。



ダウンロード時、クライアントは定期的にトラッカーにアクセスし、トラフィック統計を報告し、他のディストリビューターとダウンローダーのアドレスを受信します。 このような呼び出しが頻繁に行われるほど、トラフィックカウントはより正確になり(閉じたトラッカーの場合)、新しい配信参加者はお互いについてより速く学習します。



XBT Tracker。この投稿はC ++で記述されており、オープンおよびクローズの多くの外国のトラッカー、さらにはロシアのトラッカーでも使用されています。 もう1つの高性能トラッカーであるOpenTrackerは 、トラフィックに基づいた閉じたトラッカーをサポートしていないため、クエリの結果をデータベースに書き込む必要がないため、このコンテキストではあまり興味がありません。



ノンブロッキングI / O



90年代、ソケットを操作するとき、入出力のブロックが使用されました。recvメソッドとsendメソッドを呼び出すと、結果が期待されるまで現在のスレッドが「ハング」していました。 受信した接続ごとに、要求が処理される個別のプロセス(フォーク)が作成されました。 ただし、プロセス間でコンテキストを切り替えるには、各プロセスがスタック用のメモリとプロセッサ時間を必要とします。 負荷が小さい場合、これは恐ろしくなく、Webはその時点では完全にリクエスト/レスポンスモードでインタラクティブではなく、主にページカウントとプリミティブアンダーフォーラムがほとんどありませんでした。 Apacheはまだこの方法で動作します。 apache2では、プロセスの代わりに軽いスレッド(スレッド)を使用できますが、本質は同じです。



これに代わるものとして、1つのプロセスが多くのソケットを開いて定期的にステータスをポーリングし、たとえば、新しい接続が入ったり、読み込むデータが入ったりするなど、1つのプロセスがソケットを開くことができる非ブロッキングI / Oが登場しました-それらを提供します。 これは、たとえば、 nginxの動作そのものです。 Javaバージョン1.4以降では、このためのNIOがあります。



たとえば、TCP_DEFER_ACCEPTはデータが到着するまで接続を「遅延」できるようにするTCP_DEFER_ACCEPT、完全なHTTP要求が受信されるまで接続を遅延させるSO_ACCEPTFILTERが登場しました。 BSDのsysctl kern.ipc.somaxconnおよびLinuxのsysctl net.core.somaxconnを使用して、受け入れられない接続のキューの長さを増やす機会がありました(デフォルトでは128のみです)。これは、ソケット処理に一時停止がある場合に特に重要です。



リクエストサービス



XBTTのリクエストは非常にシンプルで、その処理には特別なコンピューティングリソースが必要ありません。必要なすべてのデータをメモリに保持するため、ソケットを操作するのと同じプロセスで実行しても問題はありません。 より深刻なタスクの場合、メンテナンスのために個別のスレッドを作成する必要があります。



解決策の1つは、処理のための要求を送信したスレッドプール(スレッドプール)を作成し、その後スレッドがプールに戻ることです。 空きスレッドがない場合、リクエストは順番に待機しています。 このアプローチにより、使用されるスレッドの総数を減らすことができ、毎回新しいスレッドを作成して、要求処理の完了後にそれを強制終了する必要がなくなります。



アクターと呼ばれるさらに優れたメカニズムは、おそらくアーラン言語とスカラ言語であり、おそらくライブラリーの形式で、他の言語で使用されます。 処理は、アクター間のメッセージの非同期転送によって実行されます。これは、すべてのユーザーに受信トレイを使用して電子メールを送信することを想定できますが、このトピックはこの投稿の範囲外です(たとえば、これに関する新しい投稿です)。



データベースのバッチ作業



XBTTトラッカーへの各呼び出しの結果は、いくつかのテーブルに記録されます。 ユーザーは、ダウンロードおよびフラッディングされたトラフィックを増やします。 トレント統計は増加しています。 現在の配布参加者のテーブルがいっぱいになります。 さらに、ダウンロード履歴を含むいくつかのサービステーブル。



従来の処理方法では、トラッカーへの要求ごとに少なくとも3つの個別のINSERTまたはUPDATEが実行され、クライアントは実行を待機するため、データベースサーバーはトラッカーへの呼び出しごとに3つの要求を実行する必要がありました。



XBTTはそれらをすぐには実行しませんが、INSERT ... VALUES(...)、(...)の大きなバンドルを蓄積します。 ...、(...)重複キーの更新f1 = VALUES(f1)、...、fN = VALUES(fN)で、数秒に1回実行されます。 そのため、データベースへのクエリの数は、トラッカーへのリクエストごとの数から1分あたりの数に減少します。 また、外部から変更される可能性のある必要なデータを定期的に再読み取りします(Webインターフェイスはトラッカーから独立しています)。



延期の重要度



このアプリケーションでは、一部のデータの損失はまったく重要ではありません。 急流のトラフィック統計が数秒でデータベースに記録されない場合、ひどいことは起こりません。 異常終了時には、蓄積されたバッファがデータベースに書き込まれますが、停電などの場合にサーバーにUPSが存在する可能性があります。 -クライアントによって転送されたすべてのデータがディスクに書き込まれる保証はありません。 バナーネットワークの場合、これも怖くないですが、すべてのデータを保存することが重要なタスクがあります。



同様に、すべてのアプリケーションがすべてのデータをメモリに保存できるわけではありません。 クライアント要求を処理するには、データベースからデータを選択する必要がある場合があります。



ただし、この場合、ブロックデータ処理は可能です。 パイプラインはいくつかの段階から組織化され(パイプライン;アクターはその実装に完全に適しています)、各段階で、十分な量(自然に、カスタム)が蓄積されるか、しばらく時間が経過すると(たとえば、10から100ミリ秒)すぐにクエリのデータグループが組み立てられます必要な数量が累積されていません-データベースに対してグループ要求が行われ、「key = value」の代わりに条件「key IN(累積リスト)」が設定されます。



これらのレコードをロックする必要がある場合、FOR UPDATE SKIP LOCKEDをクエリに追加できます(当然、データベースへの同じ接続、同じトランザクションで結果を書き込む必要があります)。 それをサポートするデータベースでPrepared Statementを使用して、クエリ分析を1回(解析)実行し、その実行に最適なプランを選択し、毎回その中のデータのみを置換できます。 そのような準備されたリクエストの数を減らすために、それに対するパラメータの数は2の累乗でのみ取得できます:1、2、4、8、16、32 ...また、リクエストをグループ化(バッチ)することもできます。一度に完了します。



All Articles