10

シナリオ

次の方法があります。

public void AddItemSecurity(int itemId, int[] userIds)
public int[] GetValidItemIds(int userId)

最初は、フォーム上のストレージを考えています:

itemId -> userId, userId, userId

userId -> itemId, itemId, itemId

AddItemSecurityサードパーティ API からデータを取得する方法に基づいてGetValidItemIdsおり、実行時にどのように使用したいかです。

2000 人のユーザーと 1000 万のアイテムが存在する可能性があります。アイテム ID の形式は次のとおりです: 2007123456、2010001234 (最初の 4 桁が年を表す 10 桁)。

AddItemSecurity超高速である必要はありませんが、GetValidIds1 秒未満である必要があります。また、既存の更新がある場合はitemId、リストに含まれなくなったユーザーの itemId を削除する必要があります。

これをどのように最適な方法で保管するかを考えようとしています。ディスク上 (キャッシュあり) が望ましいですが、コードを保守可能でクリーンなものにしたいと考えています。

MaxItemId / 8アイテム ID が 0 から始まる場合、ユーザーごとに の長さのバイト配列を作成し、アイテムが存在するかどうかを true/false ビットに設定することを考えました。これにより、配列の長さがユーザーごとに 1 MB 強に制限され、検索が高速になり、ユーザーごとにリストを簡単に更新できるようになります。これを .Net 4 フレームワークでメモリ マップ ファイルとして永続化することで、キャッシュ ロジックを自分で実装しなくても (マシンに十分な RAM がある場合)、まともなキャッシュも得られると思います。IDを解析し、年を取り除き、年ごとに配列を保存することが解決策になる可能性があります。

ItemId -> UserId[] リストをディスクに直接シリアライズし、通常の方法で読み取り/書き込みを行うFileStreamことで、リストを永続化し、変更があった場合に比較することができます。

新しいユーザーが追加されるたびに、すべてのリストも更新する必要がありますが、これは毎晩行うことができます。

質問

このアプローチを引き続き試す必要がありますか、それとも他の方法を検討する必要がありますか? 私は、SQL サーバーのパフォーマンスが十分ではなく、(少なくとも別のサーバーでホストされている場合) オーバーヘッドが発生すると考えていますが、私の仮定は間違っている可能性があります。この問題に関する考えや洞察は大歓迎です。そして、ハードウェアをあまり追加せずに解決したいと思います:)

[2010-03-31 更新]

次の条件で SQL Server 2008 をテストしました。

  • 2 つの列 (userid、itemid) を持つテーブルは両方とも Int です
  • 2 つの列のクラスター化インデックス
  • 180 人のユーザーに約 800.000 アイテムを追加 - 合計 1 億 4,400 万行
  • SQL サーバー用に割り当てられた 4GB RAM
  • デュアルコア 2.66GHz ノートパソコン
  • SSD ディスク
  • SqlDataReader を使用してすべての itemid をリストに読み込む
  • すべてのユーザーをループする

1 つのスレッドを実行すると、平均して 0.2 秒になります。2 番目のスレッドを追加すると、最大 0.4 秒になりますが、それでも問題ありません。そこから成績はどんどん落ちていきます。3 番目のスレッドを追加すると、多くのクエリが最大 2 秒かかります。4 番目のスレッドは最大 4 秒、5 番目のスレッドは一部のクエリを最大 50 秒スパイクします。

これが進行している間、CPU は 1 つのスレッドであってもルーフィングしています。私のテストアプリは、高速ループのために一部を取り、残りをSQLします。

これは、あまりうまくスケーリングできないという結論につながります。少なくとも、テスト済みのハードウェアではそうではありません。アイテムごとに 1 つのレコードではなく、ユーザーごとに int の配列を格納するなど、データベースを最適化する方法はありますか。ただし、これにより、アイテムを削除するのが難しくなります。

[2010-03-31 更新 #2]

同じデータをメモリマップファイルのビットとして入れて簡単なテストを行いました。はるかに優れたパフォーマンスを発揮します。6 スレッドのアクセス時間は 0.02 秒から 0.06 秒です。純粋にメモリバウンド。マップされたファイルは 1 つのプロセスによってマップされ、他の 6 つのプロセスによって同時にアクセスされました。また、SQL ベースが 4GB を使用したため、ディスク上のファイルは 23MB を使用しました。

4

3 に答える 3

5

多くのテストの後、メモリマップファイルを使用し、それらをスパースビット(NTFS)でマークし、C#でNTFSスパースファイルのコードを使用しました。

ウィキペディアには、スパース ファイルとは何かについての説明があります。

スパース ファイルを使用する利点は、ID の範囲を気にする必要がないことです。2006000000 から 2010999999 の間の ID のみを書き込むと、ファイルはオフセット 250,750,000 から 625,000 バイトしか割り当てません。そのオフセットまでのすべてのスペースは、ファイル システムで未割り当てです。各 ID は、セット ビットとしてファイルに保存されます。ビット配列として扱われる並べ替え。ID シーケンスが突然変更された場合は、ファイルの別の部分に割り当てられます。

どの ID が設定されているかを取得するには、OS 呼び出しを実行してスパース ファイルの割り当てられた部分を取得し、それらのシーケンスの各ビットをチェックします。また、特定の ID が設定されているかどうかの確認も非常に高速です。割り当てられたブロックの外にある場合はそこにありません。範囲内にある場合は、1 バイトが読み取られ、正しいビットが設定されているかどうかを確認するためのビット マスク チェックが行われます。

したがって、できるだけ多くの速度でチェックしたい多くの ID がある特定のシナリオでは、これが私がこれまでに見つけた最も最適な方法です。

そして良い点は、メモリー・マップされたファイルを Java と共有できることです (これは必要なものであることが判明しました)。Java は、Windows 上のメモリ マップ ファイルもサポートしており、読み取り/書き込みロジックの実装は非常に簡単です。

于 2010-06-15T06:45:55.260 に答える
1

決定を下す前に、優れたデータベースを試してみるべきだと本当に思います。このようなものは、長期的に維持するのが難しいでしょう。あなたのユーザーベースは実際にはかなり小さいです。SQL Server は、必要なものを問題なく処理できるはずです。

于 2010-03-30T14:16:15.783 に答える
0

2000 人のユーザーはそれほど悪くはありませんが、1000 万の関連アイテムがある場合は、これをデータベースに入れることを検討する必要があります。DB は、必要なすべてのストレージ、永続性、インデックス作成、キャッシュなどを実行し、非常に優れたパフォーマンスを発揮します。

また、将来のスケーラビリティも向上します。突然 200 万人のユーザーと数十億の設定に対処する必要が生じた場合でも、適切なデータベースを配置することでスケーリングは問題になりません。

于 2010-03-30T14:28:18.997 に答える