代码之家  ›  专栏  ›  技术社区  ›  Mikael Svenson

为快速查找和持久性优化数据结构存储

  •  8
  • Mikael Svenson  · 技术社区  · 14 年前

    脚本

    我有以下方法:

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

    附加安全性 不一定要表现得非常快,但是 GetValidIds 需要处于劣势。此外,如果现有的 itemId 我需要删除列表中不再存在的用户的itemID。

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

    如果项ID从0开始,我考虑创建一个字节数组,其长度为 MaxItemId / 8 对于每个用户,如果该项存在或不存在,则设置一个真/假位。这将限制每个用户的数组长度不超过1MB,并提供快速查找以及更新每个用户列表的简单方法。通过坚持这一点 Memory Mapped Files 有了.NET 4框架,我想我也可以得到不错的缓存(如果机器有足够的RAM),而不需要自己实现缓存逻辑。解析ID、剥离年份并每年存储一个数组可能是一个解决方案。

    itemID->userid[]列表可以直接序列化到磁盘,并使用普通 FileStream 以便持久化列表,并在发生更改时对其进行比较。

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

    问题

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

    [更新2010-03-31]

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

    • 具有两列(userid、itemid)的表都是int
    • 两列上的聚集索引
    • 为180个用户增加了约800.000项-共有1.44亿行
    • 为SQL Server分配了4GB RAM
    • 双核2.66GHz笔记本电脑
    • 固态硬盘
    • 使用sqldatareader将所有itemID读取到列表中
    • 循环所有用户

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

    在这过程中,CPU正在工作,即使是在一个线程上。我的测试应用程序需要一些由于快速循环,和SQL其余。

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

    [更新2010-03-31 2]

    我用同样的数据做了一个快速测试,把它作为位放在内存映射文件中。它的性能要好得多。六个线程产生0.02s到0.06s之间的访问时间。纯内存限制。映射的文件由一个进程映射,其他六个进程同时访问。当SQL数据库占用4GB时,磁盘上的文件占用23MB。

    3 回复  |  直到 14 年前
        1
  •  3
  •   Mikael Svenson    14 年前

    经过多次测试,我最终使用内存映射文件,用稀疏位(NTFS)标记它们,使用的代码来自 NTFS Sparse Files with C# .

    维基百科解释了 sparse file 是。

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

    为了检索设置的ID,我可以执行OS调用来获取稀疏文件的分配部分,然后检查这些序列中的每个位。同时检查是否设置了一个特定的ID非常快。如果它落在分配的块之外,那么它就不在那里,如果它落在里面,那么它只是一个字节的读取和一个位屏蔽检查,以查看是否设置了正确的位。

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

    好的部分是,内存映射文件也可以与Java共享(结果是需要的)。Java还支持Windows上的内存映射文件,实现读/写逻辑是相当微不足道的。

        2
  •  1
  •   ChaosPandion    14 年前

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

        3
  •  0
  •   Paul Sasik    14 年前

    2000名用户并不算太差,但有1000万个相关项目,你真的应该考虑将其放入数据库。DBS可以完成您所需要的所有存储、持久性、索引、缓存等,而且它们的性能非常好。

    它们还允许将来更好地扩展。如果您突然需要处理200万个用户和数十亿个具有良好数据库的设置,将使扩展成为一个没有问题的问题。