2

接続されたクライアントによってトリガーされる毎秒多数の MySQL クエリを実行する必要があるサーバー アプリケーションを作成しています。適切な構造化データベースとは別に、パフォーマンスを向上させる最善の方法は何でしょうか。

私の考えは、新しいクエリを実行する代わりに、データの一部をキャッシュし、キャッシュされたデータを送信することでした。データベースにさまざまなテーブルがあるように、キャッシュはこれらのさまざまな「構造」(オブジェクト) を処理する必要があります。サーバーに接続されたクライアントは間接的にデータを変更できるため、キャッシュされたデータを編集できる必要があります (そして、ある時点でデータベースを更新できる必要がありますが、これはそれほど難しくありません)。ここに私ができるようにする必要があることの短いリストがあります:

  • キャッシュされたデータ/オブジェクトを編集する
  • 古いデータ/オブジェクトを削除し、新しいデータ/オブジェクトを追加します
  • ある種の優先度 (最後に使用されたもの) でデータを並べ替える
  • ある種のIDでデータを識別します

vector または queue/priority_queue のいずれかが良い考えだと思いました。このようにして、キャッシュしたいすべてのテーブル/オブジェクトに対してキューまたはベクトルを作成できました (時間を無駄にする前に、より多くの意見を得たかったので、テストは実行しませんでした)。これらのキャッシング構造に格納される最大のオブジェクトは約 1 キロバイト (ほとんどの場合それより小さい) で、最小のオブジェクトはおそらく 96 バイトです。

キャッシング構造ごとに 50.000 を超えるオブジェクトを保存する必要はなく、10 の異なる構造 (すべてが異なるオブジェクト タイプ) で作業できると思います。

最も重要な部分は速度です。それ以外の場合は、クエリを実行するだけで済みます。クエリを作成する必要があるだけでなく、古いオブジェクトを再利用または再送信するのではなく、後で新しいオブジェクトを作成する必要があります。

だからここに私の質問があります:

  • 提供された情報に基づいてデータ/オブジェクトをキャッシュする最良の方法とその理由は何ですか?

編集:ああ、構造体を意味するときは、構造体を意味するのではなく、ベクトルキューマップなどを参照する方法がわかりませんでしたが、同時にコンテナの方が良かったかもしれません:)。

4

1 に答える 1

1

考慮すべき点はたくさんありますが、一般的には、Row Data Gatewayパターン (RDG) に基づいてリレーショナル マッピングを行います。オブジェクトの種類がそれほど多くない場合は、このアーキテクチャへのアプローチで十分に拡張できます。キャッシュのブックキーピングを Finder クラスに制限する場合、RDG はキャッシュの実装を容易にするはずです。

時間と意志があれば、Martin Fowler の Patterns of Enterprise Application Architecture をチェックしてください。お得な情報満載です。

では具体的に…

  • ある種のIDでデータを識別します

通常、これには、データベースで自動インクリメントされた整数列を使用します。unordered_map を使用して、これらのオブジェクトをキャッシュからすばやく取得できます。キャッシュ内にすべてのオブジェクトがあるため、最適化のために、find*最初にキャッシュを検索する関数をいくつか実装することもできます。検索時間が非常に制限されている場合は、 unordered_map/unordered_multimap を使用してデータの一部を「インデックス化」するか、古き良きマップ/マルチマップに固執することができます。ただし、これは作業を 2 倍にしており、データベースにはこの種のクエリが既に無料で含まれています。

  • キャッシュされたデータ/オブジェクトを編集する

ダーティ データは、実際にデータベースに書き込むまで、システムの残りの部分から見えないようにする必要があります。更新を開始し、すべてが意図したとおりに進んだ場合は、キャッシュ内のオブジェクトを更新に使用したオブジェクトに置き換えるか、単にキャッシュ内のオブジェクトを削除して、他のリーダーがデータベースから取得できるようにすることができます (結果として、オブジェクトを再度キャッシュする場合)。これは、元の Gateway オブジェクトを複製することで実装できますが、最終的には、何らかのロック戦略を実装する必要があります。

  • 古いデータ/オブジェクトを削除し、新しいデータ/オブジェクトを追加します

ここでは、単にオブジェクトをキャッシュから削除し、データベースから削除しようとします。データベースで削除が失敗した場合、他のリーダーがそれをキャッシュします。削除の処理中は、クライアントが同じレコードにアクセスできないことを確認してください。新しいレコードを追加するときは、単純に Gateway オブジェクトをインスタンス化し、それをドメイン レベル オブジェクトに渡します。変更が完了したら、Gateway オブジェクトで insert を呼び出します。新しい Gateway オブジェクトをキャッシュに入れるか、単純に最初のリーダーにキャッシュに入れさせることができます。

  • ある種の優先度 (最後に使用されたもの) でデータを並べ替える
  • 提供された情報に基づいてデータ/オブジェクトをキャッシュする最良の方法とその理由は何ですか?

これは、最適なキャッシュ アルゴリズムを選択する問題です。これは簡単に答えられる質問ではありませんが、LRU は問題なく動作するはずです。実際のメトリックがなければ正しい答えはありませんが、LRU は実装が簡単で、要件に合わない場合は、メトリックを実行して新しいアルゴリズムを決定するだけです。キャッシュへの適切なインターフェイスを用意することで、シームレスに実行できることを確認してください。もう 1 つ心に留めておくべきことは、ドメイン レベルのオブジェクトがキャッシュの制限に依存してはならないということです。10 万個のオブジェクトが必要であるのに 5 万個のキャッシュしかない場合、10 万個のオブジェクトはすべてメモリ内にありますが、そのうちの 5 万個がキャッシュ内にあります。言い換えれば、オブジェクトはキャッシュの状態に依存してはならず、また、キャッシュがあるかどうかをまったく気にするべきではありません。

次に、まだ RDG のアイデアをむき出しにしている場合は、ゲートウェイ オブジェクトをキャッシュにキャッシュしているだけです。shared_ptr を使用してゲートウェイ オブジェクトのインスタンスをキャッシュに保持できますが、ダーティ ライトを回避したい場合は、ロック戦略 (楽観的か悲観的か) も検討する必要があります。また、すべてのゲートウェイ (すべてのテーブルに 1 つ) が同じインターフェイスを継承できるため、保存/読み込み戦略を一般化できます。また、物事をシンプルに保ちながら単一のプールを使用することもできます。( boost::pool をチェックしてください。キャッシュの実装に役立つかもしれません。)

最後のポイント:

ケーキは嘘です!:D 何をしようと決めたとしても、それが適切な量のパフォーマンス指標に基づいていることを確認してください。2 か月かけてパフォーマンスを 20% 向上させた場合、ハードウェアに数ギガの RAM を追加することを検討する価値があるかもしれません。簡単に検証可能な概念実証を作成します。これにより、キャッシュの実装がうまくいくかどうかに十分な情報が得られます。そうでない場合は、テスト済みで信頼性の高い既存のソリューション (@Layne が既にコメントしているように、memcached など) を試してください。

于 2012-09-24T01:20:25.763 に答える