数据结构的最佳存储,以实现快速查找和持久性

时间:2010-03-30 14:13:50

标签: c# .net sql-server data-structures memory-mapped-files

方案

我有以下方法:

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位代表年份的10位数字。)

AddItemSecurity不必执行超快速,但GetValidIds需要亚秒级。此外,如果现有itemId有更新,我需要为列表中不再包含的用户删除该itemId。

我正在考虑如何以最佳方式存储它。最好在磁盘上(带缓存),但我希望代码可维护和清洁。

如果项目ID从0开始,我考虑为每个用户创建长度为MaxItemId / 8的字节数组,如果项目存在与否则设置真/假位。这将限制每个用户的阵列长度超过1mb,并提供快速查找以及更新每个用户列表的简便方法。通过使用.Net 4框架将其保持为Memory Mapped Files,我认为我也可以获得不错的缓存(如果机器有足够的RAM),而不是自己实现缓存逻辑。解析id,剥离年份,每年存储一个数组可能是一个解决方案。

ItemId - > UserId []列表可以直接序列化到磁盘并使用普通FileStream进行读/写,以便在发生更改时保留列表并进行区分。

每次添加新用户时,所有列表也必须更新,但这可以在每晚完成。

问题

我应该继续尝试这种方法,还是还有其他途径应该探索?我认为SQL服务器执行速度不够快,而且会产生开销(至少如果它托管在不同的服务器上),但我的假设可能是错误的。任何关于此事的想法或见解都表示赞赏。我想尝试解决它而不添加太多硬件:)

[更新2010-03-31]

我现在已经在以下条件下使用SQL Server 2008进行了测试。

  • 包含两列(userid,itemid)的表都是Int
  • 两列上的聚簇索引
  • 为180位用户添加了约800.000项 - 总计1.44亿行
  • 为SQL server分配4gb ram
  • 双核2.66ghz笔记本电脑
  • SSD磁盘
  • 使用SqlDataReader将所有itemid读入List
  • 循环所有用户

如果我运行一个线程,它的平均值为0.2秒。当我添加第二个线程时,它会上升到0.4秒,这仍然可以。从那里开始,结果正在减少。添加第三个线程会带来很多查询,最多可达2个。第四个线程,最多4秒,第五个线程在一些查询中长达50秒。

即使在一个线程上,CPU仍在进行屋顶处理。我的测试应用程序需要一些由于快速循环,而sql其余部分。

这使我得出结论,它不会很好地扩展。至少不在我测试的硬件上。有没有办法优化数据库,比如存储每个用户的int数组而不是每个项目一个记录。但这使得删除项目变得更加困难。

[更新2010-03-31#2]

我使用相同的数据进行了快速测试,将其作为内存映射文件中的位。它表现得更好。六个线程产生的访问时间介于0.02s和0.06s之间。纯粹的记忆力。映射文件由一个进程映射,并由六个其他进程同时访问。由于sql base占用了4GB,磁盘上的文件占用了23mb。

3 个答案:

答案 0 :(得分:3)

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

维基百科解释了sparse file是什么。

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

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

因此,对于您想要以尽可能快的速度检查许多身份证的特定情况,这是我迄今为止找到的最佳方式。

好的部分是内存映射文件也可以与Java共享(结果证明是必需的)。 Java还支持Windows上的内存映射文件,实现读/写逻辑非常简单。

答案 1 :(得分:1)

我真的认为你应该在做出决定之前尝试一个不错的数据库。从长远来看,这样的事情将是一个挑战。您的用户群实际上非常小。 SQL Server应该能够毫无问题地处理您需要的内容。

答案 2 :(得分:0)

2000用户不是太糟糕但是有10万个相关项目你真的应该考虑把它放到数据库中。数据库执行您需要的所有存储,持久性,索引,缓存等,并且它们的性能非常好。

它们还可以在未来实现更好的可扩展性。如果您突然需要处理200万用户,并且拥有数据库的数十亿设置将使扩展成为非问题。