c# - 数据结构的最佳存储,用于快速查找和持久化

标签 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 位数字)。

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

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

如果项目 ID 从 0 开始,我考虑为每个用户创建一个长度为 MaxItemId/8 的字节数组,并在项目存在或不存在时设置一个 true/false 位.这会将每个用户的数组长度限制在 1mb 以上,并提供快速查找以及一种简单的方法来更新每个用户的列表。通过使用 .Net 4 框架将其作为 Memory Mapped Files 持久化,我认为我也可以获得不错的缓存(如果机器有足够的 RAM)而无需自己实现缓存逻辑。解析 ID、去除年份并每年存储一个数组可能是一种解决方案。

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

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

问题

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

[更新 2010-03-31]

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

  • 有两列(userid,itemid)的表都是 Int
  • 两列的聚集索引
  • 为 180 位用户添加了约 800.000 个项目 - 总计 1.44 亿行
  • 为 SQL 服务器分配 4gb 内存
  • 双核 2.66ghz 笔记本电脑
  • 固态硬盘
  • 使用 SqlDataReader 将所有 itemid 读入列表
  • 遍历所有用户

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

在此过程中,即使在一个线程上,CPU 也处于屋顶状态。由于快速循环,我的测试应用程序需要一些,其余的用 sql 执行。

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

[更新 2010-03-31 #2]

我用相同的数据进行了快速测试,将其作为内存映射文件中的位。它的表现要好得多。六个线程产生的访问时间在 0.02 秒到 0.06 秒之间。纯粹的内存限制。映射文件由一个进程映射,并同时被其他六个进程访问。由于 sql base 占用了 4gb,磁盘上的文件占用了 23mb。

最佳答案

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

维基百科对 sparse file 的解释是。

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

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

因此,对于您有许多 ID 并希望以尽可能快的速度进行检查的特定场景,这是迄今为止我发现的最佳方式。

好的部分是内存映射文件也可以与 Java 共享(事实证明这是需要的)。 Java 还支持 Windows 上的内存映射文件,实现读/写逻辑相当简单。

关于c# - 数据结构的最佳存储,用于快速查找和持久化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2545882/

相关文章:

c# - 将属性映射到集合项

.net - SAP .NET 连接器

c# - 如何动态加载包含非托管代码的原始程序集?(绕过 'Unverifiable code failed policy check' 异常)

c# - 枚举器实现 : Use struct or class?

c# - 为什么我无法通过 C# 应用程序连接到远程 SQL 服务器?

c# - FileHelpers:非引号 CSV 中的可选字段

c# - WPF C# - 从另一个线程更新进度条

使用反射的 C# 通用 Excel 导出器

c# - 在表中查找符合特定条件的最新记录

php - Linux 上的 CakePHP 3 和 MSSQL 连接失败