LINQ对于内存集合的性能表现

8

我有一个列表:用户集合,其中包含大约10万个用户记录(所有用户对象从数据库中完全加载,包括生物、名字、姓氏等字段)。此集合在应用程序启动时从数据库中获取并保存在内存中。

然后我有类似下面的代码:

User cachedUser = users.FirstOrDefault(x => string.Equals(x.UserName, username,
StringComparison.CurrentCultureIgnoreCase));

我使用Linq从这个集合中获取用户。但是我注意到这个操作非常缓慢。在使用Linq查询大量对象的内存集合时是否存在性能问题?我应该每次需要获取用户时调用数据库吗?


7
你知道FirstOrDefault是O(n)的吧?如果你有一个非常大的集合,逐一检查每个项目会花费一定时间。(而数据库通常已经建立索引)有很多方法可以加快这个过程,最简单的方法之一就是把它放到字典里。你没有这样做的原因吗? - Kirk Woll
你只需要缓存当前用户吗?你可以使用内置的 SessionCache 对象来实现。 - Bryan Crosby
@BryanCrosby 不,我想要缓存所有用户而不是当前登录的用户。 - Rocky Singh
我不同意在会话中存储100,000个对象的列表<>. 就内存管理而言,我不认为这是一个好主意。 - scott.korin
1
@RockySingh:您是否需要在每个页面上始终显示所有用户?如果不需要,可以缓存一个子集。如果是,则可能存在极其严重的架构问题,或者您正在进行非常奇怪的操作:) - snemarch
显示剩余5条评论
4个回答

8

根据您提供的信息,我认为您可能需要重新考虑架构。利用数据库让它为您执行搜索工作。观察、测量并相应地进行更改。这样您可能会意识到自己过早的优化了整个系统。


3
如果您想优化响应时间,可以创建一个 Dictionary<T,U> 并在其中搜索用户:
    Dictionary<string, User> usersDictionary = new <Dictionary<string, User>(StringComparer.CurrentCultureIgnoreCase);

    // After querying the users from the DB add them to the dictionary             
    usersDictionary.Add(user.UserName, user);

    // Then when you need to retrieve a user
    User retrieveUser = null; 
    usersDictionary.TryGetValue(username, out retrieveUser);

希望能帮到您!

1
我认为将所有用户加载到字典中不是一个好主意。这需要大量的时间和内存。此外,如果您更改数据,还必须同步访问。 - slfan
据我理解,他无论如何都会加载数据。如果对象的内存占用较低,则不应花费太多时间或内存。它将像一个无网络延迟的内存缓存一样运作。这完全取决于最终目标是什么。 - Jason De Oliveira
他没有解释系统应该如何处理更新,所以如果他不介意数据库和内存中可能存在差异,这可能是一个好的解决方案。一切都取决于他的真正目标,我理解的是将所有用户保存在内存中。在这种情况下,最好的解决方案是使用字典。 - Jason De Oliveira
他最后的问题是每次调用时是否应该调用数据库,我认为是的。这是一个拥有10万条记录的用户数据库,我不能相信它们永远不会改变。如果他想查找特定城市的所有用户怎么办?您的字典只在搜索用户名时有效。在数据库中,您可以索引多行。 - slfan
他没有表达任何其他需要基于其他属性进行搜索的需求。如果列没有被索引,数据库也会花费更多时间(而且你不能在所有列上建立索引)。我不反对调用数据库。我只是指出,如果他需要一个内存解决方案,最好的方法是使用字典。 - Jason De Oliveira
显示剩余2条评论

3
您的LINQ查询类似于任何其他迭代技术(循环,数组搜索),它将访问每个记录,直到找到请求的记录为止。在最坏的情况下,这意味着要进行100,000次比较。为了使此过程更快,您有以下选项:
  1. 使用排序列表或字典:二进制搜索速度更快。在从数据库提取数据时使用ORDER BY对数据进行排序
  2. 使用DataSet。它就像是一个内存中的数据库,提供更快的搜索
  3. 保留数据在数据库中,并设置适当的索引以实现更快的访问

我建议使用数据库,原因如下:

  • 存储100k条记录是浪费内存的,而且您可能永远不会使用它们
  • 一旦更改数据,您将不得不刷新缓存,这可能相当复杂
  • Web应用程序是多线程的(每个请求在自己的线程中运行)。如果更改数据,则必须使用锁进行同步。
  • 数据库可以缓存频繁调用的数据
  • 您需要编写的代码更少
  • 您拥有一个无状态的Web应用程序,它可以更好地扩展(Web Farm)
  • 您的应用程序可能有其他数据,您无法将所有内容存储在内存中

我的问题是,由于有太多的记录,即使是数据库访问也很慢。所以我想,为什么不将所有用户缓存在内存中,因为我已经在使用事件来管理缓存对象,当用户更新其个人资料时。我们不能在内存缓存中建立索引吗? - Rocky Singh
3
您使用的是哪种数据库?如果对搜索行设置索引,那么10万条记录并不算多。在Web应用程序中我永远不会将如此多的记录存储在内存中。 - slfan
我们数据库中的10万条数据可能很快就会达到100万。关键是无论如何,RAM始终比物理数据文件更快。因此,为什么不在代码中使用基于RAM的集合进行高性能算法搜索,而不是依赖于数据库呢? - Rocky Singh
2
因为数据库中的搜索是基于索引(二进制)而不是顺序的。因为数据库也会在内存中缓存数据(可能以更有效的方式)。而且,因为您可能想要更改数据,您必须进行同步。千万不要这样做!如果设计得好,访问数据库的时间应该只有几毫秒。 - slfan
2
将100万个用户缓存在内存中是一种浪费内存的行为,而这些内存可以用于更好的用途。即使有100万个用户,也应该通过优化的数据库在毫秒级别内检索用户。 - Gene S

0
你注意到的搜索性能差异是因为数据库使用索引来定位数据库中的字符串,而在内存中,你只需搜索所有记录,直到找到匹配项。此外,数据库会为字符串保留哈希值,并搜索这个哈希值,这样速度更快,不需要进行实际的字符串比较。 Dictionary<> 也会建立索引,但添加数据时会有延迟,因为每次添加数据时都要搜索正确的索引点来放置它。
此外,数据库缓存结果,许多数据库还会缓存索引并创建额外的统计信息,以帮助快速定位所需内容。
除非你可以为特定情况提供更快的解决方案,否则最好让数据库进行搜索。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接