近日在一个大型Web项目中,采用Linq to Sql替换原来的sqlcommand/sqldatareader方式来获取数据,上线后刚开始一切正常,但是随着访问量的增加,网站明显慢了很多,监测服务器CPU占用率/内存使用情况等性能指标却发现均在正常范围内,无意中在SqlServer Profier中跟踪数据库执行的sql语句时,发现有大量语句直接将整个表的数据全部提取出来了,而非仅返回分页中的当前页数据!

而这些SQL都是Linq自动翻译并最终提交到数据库的,查看了相关的代码,明明写着Skip(n).Take(m)类似的语句,为何还会生成这么“傻”的sql呢?

于是写了以下测试代码[测试环境:vs.net2008 + sqlsever2005 + win2003],最终发现是Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);使用后,导致这个问题的产生


1.测试表T_Test:

linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!CREATE TABLE [dbo].[T_Test](
linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!    
[F_ID] [int] IDENTITY(1,1NOT NULL,
linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!    
[F_Name] [nvarchar](50) COLLATE Chinese_PRC_CI_AS NULL,
linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!    
[F_Age] [int] NULL,
linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了! 
CONSTRAINT [PK_T_Test] PRIMARY KEY CLUSTERED 
linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!(
linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!    
[F_ID] ASC
linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!)
WITH (IGNORE_DUP_KEY = OFFON [PRIMARY]
linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!
ON [PRIMARY]

 


录入了几条测试数据:
F_ID F_Name F_Age
15 Jimmy 20
16 Mary 14
17 Jack 30
18 张三 35
19 李四 24

2.新建一个"控制台应用程序",把T_Test拖到dbml中,Program.cs文件中输入如下代码:
 1linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!using System;
 2linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!using System.Collections.Generic;
 3linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!using System.Linq;
 4linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!using System.Linq.Expressions;
 5linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!using CNTVS.LINQ;
 6linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!
 7linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!namespace TestLinq
 8}

代码很简单,找出F_Name中包含字母"J",F_Age大于20的记录,并且跳过第一个后,仅获取一条记录

注:PredicateBuilder是一个老外写的用于动态构造Expression表达式的工具类,在查询条件不确定,需要动态创建时,非常有用,完整代码如下:


}

以下是输出结果:

Name:Jimmy      ,Age:20


用Sql Server Profiler跟踪提交到数据库的语句为:

linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!exec sp_executesql N'SELECT [t1].[F_ID], [t1].[F_Name], [t1].[F_Age]
linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!FROM (
linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!    SELECT ROW_NUMBER() OVER (ORDER BY [t0].[F_ID], [t0].[F_Name], [t0].[F_Age]) AS [ROW_NUMBER], [t0].[F_ID], [t0].[F_Name], [t0].[F_Age]
linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!    FROM [dbo].[T_Test] AS [t0]
linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!    WHERE ([t0].[F_Age] >= @p0) AND ([t0].[F_Name] LIKE @p1)
linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!    ) AS [t1]
linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!WHERE [t1].[ROW_NUMBER] BETWEEN @p2 + 1 AND @p2 + @p3
linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!ORDER BY [t1].[ROW_NUMBER]
',N'@p0 int,@p1 nvarchar(3),@p2 int,@p3 int',@p0=20,@p1=N'%J%',@p2=1,@p3=1

一切都很完美,跟我们想象的一样仅取了一条记录


3.但是,我们稍微把代码改一下:

把Main方法中的前三行注释去掉,同时把var Data = GetData(1, 1);注释掉,即


 1linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!static void Main(string[] args)
 2        }

修改的用意在换一种方法(即Where<T>(Expression))取数据,运行后输出结果跟上一种方式完全相同,而且这种方式可以在调用方法前动态创建需要的查询条件表达式,用法更灵活,但是我跟踪到的sql语句却是:
SELECT [t0].[F_ID][t0].[F_Name][t0].[F_Age]
FROM [dbo].[T_Test] AS [t0]

即采用Where<T>(Expression)方式取数据时,居然先把所有数据取回来,再利用Expression来进行结果筛选以及Skip/Take操作,真是令人大跌眼镜!(或许仅仅是我水平有限,理解不了而已),这样的方式,在单表数据量很大时,性能当然极低。

恳请园子里的哪位linq达人,能解释一二?


知道了最终结果,处理方法自然也就明朗了,当时为了快速解决问题,只能把这类操作回归到最原始的SqlCommand/SqlDataReader方式读取,也许有更好的办法,欢迎大家指点。

相关文章:

  • 2021-09-18
  • 2022-12-23
  • 2021-09-18
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2021-11-06
  • 2022-02-24
  • 2022-12-23
  • 2022-02-07
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案