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(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。

4

3 回答 3

5

经过大量测试后,我最终使用了内存映射文件,用稀疏位 (NTFS) 标记它们,使用来自NTFS Sparse Files 的代码和 C#

Wikipedia 解释了什么是稀疏文件

使用稀疏文件的好处是我不必关心我的 id 在什么范围内。如果我只写 2006000000 和 2010999999 之间的 id,那么文件将只从文件中的偏移量 250,750,000 分配 625,000 字节。直到该偏移量的所有空间都未在文件系统中分配。每个 id 都作为一个设置位存储在文件中。排序被视为一个位数组。如果 id 序列突然改变,那么它会分配到文件的另一部分。

为了检索设置了哪些 id,我可以执行操作系统调用以获取稀疏文件的分配部分,然后检查这些序列中的每一位。检查是否设置了特定的 id 也非常快。如果它落在分配的块之外,那么它就不存在,如果它落在里面,它只是读取一个字节并检查位掩码以查看是否设置了正确的位。

因此,对于您想要以尽可能快的速度检查许多 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 回答