【问题标题】:In-memory search index for application takes up too much memory - any suggestions?应用程序的内存搜索索引占用太多内存 - 有什么建议吗?
【发布时间】:2010-09-18 08:12:16
【问题描述】:

在我们的桌面应用程序中,我们使用inverted index 实现了一个简单的搜索引擎。

不幸的是,我们的一些用户数据集可能会变得非常大,例如在创建倒排索引之前占用约 1GB 的内存。倒排索引本身占用了大量内存,几乎与被索引的数据一样多(另外 1GB 的 RAM)。

显然,这会产生内存不足错误的问题,因为每个应用程序 2GB 内存的 32 位 Windows 限制受到限制,或者使用较低规格计算机的用户难以应对内存需求。

我们的倒排索引存储为:

Dictionary<string, List<ApplicationObject>>

这是在处理每个对象时在数据加载期间创建的,以便将 applicationObject 的键字符串和描述词存储在倒排索引中。

所以,我的问题是:是否可以在空间方面更有效地存储搜索索引?也许需要使用不同的结构或策略?或者是否可以创建一种 CompressedDictionary?由于它存储大量字符串,我希望它具有高度可压缩性。

【问题讨论】:

    标签: c# optimization search memory search-engine


    【解决方案1】:

    如果它是 1GB……把它放在磁盘上。使用像 Berkeley DB 这样的东西。它仍然会非常快。

    这是一个为其提供 .net 接口的项目:

    http://sourceforge.net/projects/libdb-dotnet

    【讨论】:

    • 如果可能,我想避免这种情况,因为拥有内存搜索索引会更简单。但也许这是不可能的,但它似乎应该对我来说是可能的。
    【解决方案2】:

    我看到了一些解决方案:

    1. 如果您将 ApplicationObjects 放在一个数组中,则只存储索引 - 可能会更小。
    2. 您可以使用一点 C++/CLI 来存储字典,使用 UTF-8。
    3. 不要费心存储所有不同的字符串,使用Trie

    【讨论】:

    • 对于第 1 点)它们没有存储在数组中,但是您的意思是存储索引而不是字符串键吗?那么如何通过字符串进行搜索呢?还是您的意思是使用 List 代替 List?我想它可能会更小,但可能不会很大。
    【解决方案3】:

    我怀疑你可能会发现你有很多非常小的列表。

    我建议您大致了解一下频率是什么样的 - 您的字典条目中有多少具有单个元素列表,有多少具有两个元素列表等。您可能会存储几个单独的字典 - 一个用于“我只有一个元素”(直接映射)然后“我有两个元素”(映射到带有两个引用的 Pair 结构)等等,直到它变得愚蠢 - 很可能在大约 3 个条目处 - 此时你回到正常列表.将所有内容封装在一个简单的界面后面(添加条目/检索条目)。这样一来,浪费的空间就会少很多(主要是空缓冲区、计数等)。

    如果这些都没有多大意义,请告诉我,我会尝试编写一些代码。

    【讨论】:

    • 这是一个有趣的观察...是的,我认为大多数列表都会非常小。根据您的建议,我猜倒排索引的创建需要更长的时间,因为您必须在 1-item、2-item 等字典之间移动项目,但可能会节省空间。
    • 老实说,我怀疑性能上的差异会非常小 - 但是是的,会有一些开销。绝对值得在编码之前检查分布:)
    • 一个想法可能会降低一开始的成本:只需要一个 Dictionary>。这意味着每个值都有一个对象,而不是使用结构来避免这种情况,但是您只需要替换字典中的条目而不是执行删除/添加
    • 没错,我明天会试试这个并报告它有什么不同。
    • 我进行了调查,认为这种变化会产生一些影响,尽管影响不大。例如大约 50% 的列表长度为 1、2 或 3 个元素。但总体而言,这些列表约占元素总数的 5%。
    【解决方案4】:

    我同意 bobwienholt 的观点,但如果您正在索引数据集,我假设这些数据来自某个地方的数据库。仅使用 DTSearchLucene.net 之类的搜索引擎进行搜索是否有意义?

    【讨论】:

    • 也许,但我想这会更复杂?即应用程序对象存储在许多不同的表中,这些表映射到不同的特定应用程序对象。啊,我们的应用程序也被缓冲了,因此内存中的数据集可能与数据库不同步。
    【解决方案5】:

    您可以采用 Lucene 所做的方法。首先,您创建一个随机访问内存流(System.IO.MemoryStream),该流镜像磁盘上的一个,但只是其中的一部分(如果您有错误的部分,请从磁盘加载另一个) .这确实会让人头疼,您的字典需要一种文件可映射格式。维基百科有对paging technique 的描述。

    关于文件可映射的场景。如果您打开 Reflector 并反映 Dictionary 类,您将看到它由存储桶组成。您可能可以将这些存储桶中的每一个用作页面和物理文件(这样插入速度更快)。然后,您还可以通过简单地将“项目 x 已删除”值插入文件并经常清理文件来松散地删除值。

    顺便说一句,桶保存具有相同哈希值的值。您存储的值覆盖 GetHashCode() 方法非常重要(编译器会警告您有关 Equals() 的情况,因此也要覆盖它)。如果这样做,您的查找速度会显着提高。

    【讨论】:

      【解决方案6】:

      如何使用 Memory Mapped File Win32 API 透明地备份您的内存结构?

      http://www.eggheadcafe.com/articles/20050116.asp 具有启用它所需的 PInvokes。

      【讨论】:

      • 从 .NET Framework 版本 4 开始,您可以使用托管代码访问内存映射文件,其方式与本机 Windows 函数访问内存映射文件的方式相同,如管理内存映射文件中所述在 MSDN 库中的 Win32 中。 msdn.microsoft.com/en-us/library/dd997372.aspx
      【解决方案7】:

      索引是仅添加到其中还是从中删除键?

      【讨论】:

      • 如果引用的 ApplicationObject 被删除,则应从索引中删除键。
      猜你喜欢
      • 2011-09-20
      • 2017-09-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-10
      • 1970-01-01
      • 2011-03-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多