Entity Framework Code First 在Object Join Linq查询时出现全表查询的语句。

最近一个项目,使用微软的Entity Framework的ORM框架的项目,部署到现场后,出现了系统缓慢,多个客户端的内存溢出崩溃的问题。

打开了SQL Server Profiler(SQL Server Profiler的简单使用)排查,发现有全表查询的语句,这表中有上万条数据,所以客户端查询后内存溢出了。

从代码中排查是否有直接全表查询的语句,结果未找到,后来在网上搜索到Linq to Object 连接(join) Linq to Entity时可能会引起全表查询。(https://www.cnblogs.com/gxlinhai/p/4263393.html)

所以这里也创建了一个demo做了进一步的了解。

此Demo 使用Entity Framework的ORM框架,只需要配置数据库连接,能自动生成数据库,并创建一些数据。

表有Entity和Foo两个表,其中Foo表中有Entity表的外键。

打开SQL Server Profiler检测生成的语句。

测试一,用IEnumerable来连表查询。

                IEnumerable<Foo> foos = db.Foos;
var objectNames = (from foo in foos
join en in db.Entitys
on foo.EntityId equals en.EntityId
select foo.Name).ToList();

之后在SQL Server Profiler中监测到如下语句:

SELECT
[Extent1].[EntityId] AS [EntityId],
[Extent1].[Name] AS [Name],
[Extent1].[Notes] AS [Notes]
FROM [dbo].[Entities] AS [Extent1]

出现了Entity表全表查询的语句。

测试二, 用var来连表查询。

                var foos = db.Foos;
var objectNames = (from foo in foos
join en in db.Entitys
on foo.EntityId equals en.EntityId
select foo.Name).ToList();

在SQL Server Profiler中,监测到的语句如下

SELECT
[Extent1].[Name] AS [Name]
FROM [dbo].[Foos] AS [Extent1]
INNER JOIN [dbo].[Entities] AS [Extent2] ON [Extent1].[EntityId] = [Extent2].[EntityId]

没有出现全表查询的语句。

测试三,用IQueryable来连表查询

IQueryable<Foo> foos = db.Foos;
var objectNames = (from foo in foos
join en in db.Entitys
on foo.EntityId equals en.EntityId
select foo.Name).ToList();

在SQL Server Profiler中,监测到的语句如下

SELECT
[Extent1].[Name] AS [Name]
FROM [dbo].[Foos] AS [Extent1]
INNER JOIN [dbo].[Entities] AS [Extent2] ON [Extent1].[EntityId] = [Extent2].[EntityId]

没有出现全表查询的语句。

测试四:结合别人说的代码,修改如下

 List<MyObject> objectList = new List<MyObject>();
objectList.Add(new MyObject { Identity = 1, Name = "Jack", Age = 30 });
objectList.Add(new MyObject { Identity = 2, Name = "Sam", Age = 28 });
objectList.Add(new MyObject { Identity = 3, Name = "Lucy", Age = 23 }); var objectNames = (from foo in objectList
join en in db.Entitys
on foo.Identity equals en.EntityId
select foo.Name).ToList();

在SQL Server Profiler中,监测到的语句如下

SELECT
[Extent1].[EntityId] AS [EntityId],
[Extent1].[Name] AS [Name],
[Extent1].[Notes] AS [Notes]
FROM [dbo].[Entities] AS [Extent1]

又出现了Entity全表查询的语句。

结论:

测试1:返回结果为IEnumerable时,有可能数据已经缓存到内存里了,作为了object,这样再linq查询时,不会重新调整sql语句,导致了连接表全表查询后在内存中过滤。

测试2、3: IQueryable在连表查询时,最后会重新生成sql语句来查询。var默认为DbSet,DbSet也会重新生成sql语句来查询。

测试4: 直接证明了object与linq查询,导致查询语句会变成全表查询。

Demo见如下:

百度网盘:https://pan.baidu.com/s/1cDEIS2

提取密码:qwnb

上一篇:ReactiveCocoa / RxSwift 笔记一


下一篇:spark重点知识