前几天是临危受命,跟着师哥一起解决数据查询太慢的问题。最近时间太少,没有静下心来好好总结一下,都过了一个多星期了,再不写点东西就快忘了。- -
一开始的时候我们都认为数据查询太慢是WCF传输得问题,所以师哥带着我们从最简单的开始,把现在用的框架逐个击破。我们的框架使用EF进行对数据库的操作,首先就分成了两组,一组做使用EF查询的Demo,一组使用ADO.Net写了一个SQLhelper进行查询。
这两组查询的结果,总共60万条数据,全部查询出来都是10秒多一点,从监控的时间来看是差不多的,EF会慢一点。之后进行了分页查询模拟,仅在60万条数据中查询前10条,检测到的数据在零点几秒,时间差很小可以忽略不计。
到这里我们确定了,查询缓慢并不是使用了EF的问题,再然后我们在前两个Demo的基础上加入了WCF,再次测试之后,发现就算是经过WCF,查询的速度仅仅比不用WCF多了1秒。现在看来,并不是WCF的问题,那么问题到底在哪呢?
我们在项目中使用的查询,是继承了底层封装好的查询功能,那么我们就直接使用了现在系统中的底层查询方法。结果很奇怪,在更换了基础的数据库之后,里面总共有4万条数据,查询所有数据的时间是6秒,而查询前10条的结果却是26秒,查询前十条数据怎么会比查询所有的还慢呢?而且还慢了20秒。
那么现在我们的眼光就订到了底层的分页查询上了,询问了熟悉底层方法的人了解到了一个情况,底层从来没有实现分页查询,从来都是查询出来多有的数据,然后根据分页擦查询的参数进行筛选,选出应该看到的那十条数据,换句话说,假分页。
如果我们不用底层呢?直接使用EF进行查询,查询条件都一直的情况下,查出10条的时间是2秒多。现在看来,是底层的问题。
根据师哥的指导,我们分别对使用底层和不使用底层两种查询方式,进行了数据库的监测,看了一下EF发给SQL Server的语句。
使用底层查询的SQL语句是这样的。
SELECT
[Extent1].[Operator] AS [Operator],
[Extent1].[TimeStamp] AS [TimeStamp],
[Extent1].[IsEnabled] AS [IsEnabled],
[Extent1].[OnClassStudentID] AS [OnClassStudentID],
[Extent1].[OnClassID] AS [OnClassID],
[Extent1].[StudentID] AS [StudentID]
FROM[dbo].[BasicOnClassStudentEntities] AS [Extent1]
很明显能读出来这个是查询所有的数据。而是用EF进行查询的语句是这样的。
SELECT TOP (10)
[Extent1].[Operator] AS [Operator],
[Extent1].[TimeStamp] AS [TimeStamp],
[Extent1].[IsEnabled] AS [IsEnabled],
[Extent1].[OnClassStudentID] AS [OnClassStudentID],
[Extent1].[OnClassID] AS [OnClassID],
[Extent1].[StudentID] AS [StudentID]
FROM ( SELECT [Extent1].[Operator] AS [Operator], [Extent1].[TimeStamp] AS [TimeStamp], [Extent1].[IsEnabled] AS [IsEnabled], [Extent1].[OnClassStudentID] AS [OnClassStudentID], [Extent1].[OnClassID] AS [OnClassID], [Extent1].[StudentID] AS [StudentID], row_number() OVER (ORDER BY [Extent1].[StudentID] ASC) AS [row_number]
<span style="white-space:pre"> </span>FROM [dbo].[BasicOnClassStudentEntities] AS [Extent1]
) AS [Extent1]
WHERE [Extent1].[row_number] > 159990
ORDER BY [Extent1].[StudentID] ASC
现在看来就有点眉目了,查询的条件没有传给EF,但是为什么查询出来的结果依然是正确的呢?这个当时是没有解决的一个疑问,之后我会说明,继续我们的故事。
这个时候,我又尝试了一次条件查询,发现查询的结果是一样的,数据库监控到的是查询所有,但是返回的结果就只用那么几条数据,两个方法相同的参数有一个,lambda表达式。
当我们使用lambda表达式进行查询的时候,如果直接给EF,那么EF会根据这个表达式生成SQL语句。而是用底层的时候lambda表达式出了问题,没有被识别,就说明了传递参数的过程中出现了问题,参数传递的不对。
之后我们进行了资料查询,最终得到了解决的方案,在传递lambda表达式的时候,参数类型使用的是Func<TSource,boll>,当我们把这个参数类型改为Expression<Func<TSource, bool>>,问题解决了,秒查。
这两天我在做总结,发现了一片同样的博客,内容也是这个,里面解释了为什么会查询所有。
将Func类型的变量作为参数传给Where方法进行LINQ查询时,Enitity Framework会产生全表查询,将整个数据库表中的数据加载到内存,然后在内存中根据Where中的条件进一步查询。(http://www.cnblogs.com/dudu/archive/2012/04/01/enitity_framework_func.html)
我们遇到的所有问题都是有人遇到的,解决不了不是什么问题,但是我们一直忍受着缓慢的查询就是我们的问题了,还记得老师说过不将就是发现的原动力,这次3.0的项目中我们将就了那么长时间,最终也只是一个简单的小问题,哎,都把老师说得话都忘了啊。
下次总计总结Expression<Func<TSource, bool>>和Func<TSource,boll>到底都是什么意思,看看为什么这两个会引起查询上的区别。