我使用 LINQ 起草了 2个ASP.NET应用程序。一个连接到 MS SQL Server ,另一个连接到某些专有内存结构。 这两个应用程序都使用 3 int fields 表, 500 000条记录(内存结构与SQL Server表相同)。使用的控件是常规的: GridView 和 ObjectDataSource 。 在应用程序中,我计算每次分页点击处理所需的平均时间。
这是令人震惊的结果。为什么应用程序处理内存中的数据比使用硬盘驱动器的应用程序慢8倍?任何人都可以告诉我为什么会这样吗?
答案 0 :(得分:4)
主要因素可能是算法效率。 LINQ-to-Objects与IEnumerable<T>
输入和输出一起使用,这些输入和输出通常按顺序处理,而数据库可能具有引起大幅加速的索引。
答案 1 :(得分:0)
我至少可以想到三个原因:
<强>索引强>
如果在正确编制索引的数据库上运行,但是如果在内存中迭代列表,则会有很多类型的查询运行得非常快。例如,ID(主键)的查找在数据库中几乎是即时的,因为结果存储在具有非常小的高度的B树中。要在内存中的列表中查找相同的元素,需要扫描整个列表。
<强>缓存强>
您的假设是数据库始终会访问磁盘。这并非总是如此。数据库将尝试在内存中保存尽可能多的数据,因此当您向它请求数据时,它已经为您准备好了答案。特别是它将在内存中保存常用的索引,并在必要时仅访问磁盘。数据存储在磁盘和内存中的方式也经过了精心优化,以减少磁盘搜索和页面未命中。
<强>优化强>
即使没有索引,数据库仍然知道许多可以加快速度的技巧。例如,如果在SQL Server中执行以下操作:
list.OrderBy(x => x.Value).Take(1)
如果列表中有索引,它几乎是即时的,但即使没有索引,它也会使用一个名为TOP N SORT
的特殊优化,它在线性时间内运行。检查查询的执行计划,以查看是否正在使用此优化。请注意,LINQ to Objects未实现此优化。我们可以通过运行以下代码来看到这一点:
Random random = new Random();
List<Foo> list = new List<Foo>();
for (int i = 0; i < 10000000; ++i)
{
list.Add(new Foo { Id = random.Next() });
}
DateTime now = DateTime.UtcNow;
Foo smallest = list.OrderBy(foo => foo.Id).First();
Console.WriteLine(DateTime.UtcNow - now);
此代码执行大约需要30秒,并且随着更多项目的添加,执行时间会比线性增长慢。用这个替换查询会导致不到一秒的时间:
int smallestId = list.Min(foo => foo.Id);
这是因为在LINQ中,对象OrderBy
是使用O(n log(n))
算法实现的,但Min
使用O(n)
算法。但是,当针对SQL Server执行时,这两个查询都将生成相同的SQL,并且两者都是线性时间 - O(n)
。
因此,在数据库中运行像OrderBy(x => x.Something).Skip(50).Take(10)
这样的分页查询会更快,因为需要付出更多的努力来确保它更快。毕竟,这种查询的速度是数据库的主要卖点。