シナリオ
次の方法があります。
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
超高速である必要はありませんが、GetValidIds
1 秒未満である必要があります。また、既存の更新がある場合は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 を使用しました。