设想
我有以下方法:
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(10 位数字,其中前四位代表年份)。
AddItemSecurity
不必执行超快,但GetValidIds
需要亚秒级。此外,如果现有的有更新,itemId
我需要为不再在列表中的用户删除该 itemId。
我正在尝试考虑如何以最佳方式存储它。最好在磁盘上(带有缓存),但我希望代码可维护且干净。
如果项目 id 从 0 开始,我考虑MaxItemId / 8
为每个用户创建一个长度为 的字节数组,并在项目是否存在时设置一个真/假位。这会将每个用户的数组长度限制为略高于 1mb,并提供快速查找以及更新每个用户列表的简单方法。通过使用 .Net 4 框架将其作为内存映射文件持久保存,我认为我也可以获得不错的缓存(如果机器有足够的 RAM),而无需自己实现缓存逻辑。每年解析 id、剥离年份并存储一个数组可能是一种解决方案。
ItemId -> UserId[] 列表可以直接序列化到磁盘并以正常方式读取/写入,FileStream
以便持久化列表并在发生更改时对其进行比较。
每次添加新用户时,所有列表也必须更新,但这可以每晚完成。
问题
我应该继续尝试这种方法,还是应该探索其他路径?我认为 SQL 服务器的执行速度不够快,并且会产生开销(至少如果它托管在不同的服务器上),但我的假设可能是错误的。感谢您对此事的任何想法或见解。我想尝试在不添加太多硬件的情况下解决它:)
[2010-03-31更新]
我现在已经在以下条件下使用 SQL Server 2008 进行了测试。
- 具有两列 (userid,itemid) 的表都是 Int
- 两列的聚集索引
- 为 180 个用户添加了约 800.000 个项目 - 总共 1.44 亿行
- 为 SQL 服务器分配 4gb 内存
- 双核 2.66GHz 笔记本电脑
- SSD盘
- 使用 SqlDataReader 将所有 itemid 读入列表
- 遍历所有用户
如果我运行一个线程,它平均为 0.2 秒。当我添加第二个线程时,它会上升到 0.4 秒,这仍然可以。从那里开始,结果正在减少。添加第三个线程会使查询次数达到 2 秒。第四个线程最多 4 秒,第五个线程使某些查询达到 50 秒。
在这种情况下,CPU 正在运行,即使在一个线程上也是如此。由于快速循环,我的测试应用程序需要一些时间,其余部分需要 sql。
这使我得出结论,它不会很好地扩展。至少在我测试过的硬件上没有。有没有办法优化数据库,比如为每个用户存储一个 int 数组,而不是每个项目一个记录。但这使得移除项目变得更加困难。
[更新 2010-03-31 #2]
我使用相同的数据进行了快速测试,将其作为内存映射文件中的位。它的表现要好得多。六个线程产生的访问时间在 0.02 秒到 0.06 秒之间。纯内存绑定。映射文件由一个进程映射,并由另外六个进程同时访问。由于 sql base 占用了 4gb,磁盘上的文件占用了 23mb。