EF性能优化
下面总结了一些在使用EF的过程中应当特别注意的地方,避免大家再走弯路。
1、分清真分页和假分页
大家都知道分页分为真分页和假分页,并且假分页是特别耗费性能的。我们在使用的过程中也是以真分页为主,但是在使用EF写分页语句的时候,稍有不慎,真分页便会成为假分页:
query.ToList().Skip((PageIndex - 1) * PageSize).Take(PageSize);
query.Skip((PageIndex - 1) * PageSize).Take(PageSize).ToList();
上面两句话乍一看差不多,并且都会实现我们分页的需求。但是这两句的执行过程,生成的sql语句大有不同。第一条语句的执行过程是:
a.先把数据全部都查询出来,放到内存中
b.转换成List
c.从List中进行分页操作,查询出结果。 这其实是假分页的效果。
而第二条语句就是真分页了,将参数传到数据库,生成分页sql语句,在数据库中查询出结果。
2.合理使用EF的加载方式
选择什么样的数据加载方式需要因时而异,如果选择不当很可能会影响系统性能,每一种数据加载方式都有它存在的意义,但目的只有一个,那就是以最少的代价获取到需要的数据。
Lazy Loading and Eager Loading
EF加载数据的方式,小编了解的有预加载、延迟加载、显式加载、按需加载。不同的加载方式都有不同的适用情况,我们不能在这里笼统地下决定说哪种方式好哪种方式不好。但有一点是需要遵循的,那就是如何提高数据加载的效率。今天这篇博客就来介绍一下EF 的预加载和延迟加载。
延迟加载(Lazy Loading)
延迟加载又叫惰性加载(Lazy Loading):即在需要或者使用的时候加载数据。默认情况下,EF会使用延迟加载方式加载数据,即数据库上下文的属性:Configuration.LazyLoadingEnabled = true;
如果将数据库上下文的属性设置为 false 的话(Configuration.LazyLoadingEnabled = false; )将不会查询到从表的数据,只会执行一次查询。
预加载(Eager Loading)
如果你想让所有数据一次性全部加载到内存中,那么你需要使用.Include(Entity)方法。
比较两种加载方式
预加载:
• 减少数据访问的延迟,在一次数据库的访问中返回所有的数据。
• 减少与数据库的交互次数
延迟加载:
• 非常宽容,因为只在需要的时候加载数据,不需要预先计划
• 可能会因为数据访问的延迟而降低性能,考虑到每访问父实体的子实体时,就需要访问数据库。
两种加载数据的方式没有什么好坏之分,只是从不同的角度出发适用于不同情况环境。延迟加载更具有灵活性,类似于分治法,每次加载少量数据,分多次加载。但是当主表数据数量过多时,会频繁访问数据库降低性能。预加载只要访问一次数据库就可以拿到全部的数据,放到内存中。但是当数据量很多,或者实体级联关系复杂时要特别注意性能了;实体关系复杂时EF自动生成的sql语句可能会非常复杂,这点也要特别注意。
显式加载(Explicit Loading)
显式加载和延迟加载非常类似,不同的是显式加载要手动关闭EF的延迟加载属性,通过代码ctx.Configuration.LazyLoadingEnabled = false;来完成。
按需加载
其实EF并不存在按需加载的概念,但是这种方式很值得说一说,在加载数据的时候并不是需要所有的属性值,可能只需要一个Id,Name值。所以我们可以对要查询的实体进行一下筛选,只加载自己需要的某些列,避免加载大量的垃圾数据。在这里按需加载的概念只是加载需要的列。
#region 按需加载:查询部分列数据 //查询表中部分列的数据
var items = from c in dbcontext.Customer where c.Id < 10 select new { Id = c.Id, CName = c.CusName, OrderCount = c.Order.Count() }; foreach (var item in items) { Console.WriteLine(item.CName); }
#endregion
3.注意事务的简短性
在使用事务时,我们尽量要把与事务无关的东西放到事务外执行,比如(查询语句或者其他事务外的语句),如果让一个事务的执行时间过长,很容易引起资源死锁的问题,当用压力测时,马上就出现资源被锁的错误。
4.NoTracking的使用
查询出来的实体,如果不需要删除和修改,请用NoTracking查询。
using (var context = new MyDbContext())
{
var people = context.People.Where(p => p.PersonID > 100).AsNoTracking().ToList();
}
有时我们的实体只需要显示,无需更新,所以为了提高性能,我们不需要实体被EF context追踪。此时可以使用NoTracking的查询来得到实体,这样实体的状态会是Detached状态。
5.对于逻辑相对复杂的查询,要随时监控生成的Sql语句。
毕竟EF生成的语句,往往比我们生成的语句更加复杂,这个时候我们就要考虑是否通过其他方式来提高性能。比如自己写原生的sql语句,有时候原生SQL语句是更好的选择。另外我们要善于使用SQL Server Profiler这个工具,实时监控生成的sql语句。
6.批量删除和修改
不知道你是否研究过EF的插入删除和修改操作,当你批量操作数据的时候,通过SQL Server Profiler可以明显看到产生了大量的Insert,Update语句,效率非常低;因为他插入一条数据,会对应生成一条Insert语句,当你的list中有10万条数据时,就会生成10万条插入语句!不过还好咱们有对策:Entity Framework Extendeds ,EF扩展类完美解决批量操作问题。
因为系统数据量暂时不大的原因,做了这些改变之后,性能提升并不是特别明显。但是积少成多,一点点的优化或进步我们都要争取。当系统数据量到了一定程度后,你会发现,这一点点的改变带给你的效果,将不只是一点点。
优化方法总结
1.连接保持畅通
意思是不要在需要的时候连接了不需要的时候断开,需要了又去连接(特殊情况除外),目的在于减少对数据库的操作。
2.关闭EF的一些配置
EF使用时,会在Config中配置,对于使用CRUD功能,有一些是用不到的,可以关闭,关闭后的测试效果加快几秒,略微提升。
3.存入List
EF支持AddRange,如果需要存5条数据,将这5条数据放入list一次存入,要比一条一条的存大大的快。
4.查找数据用Linq or Lamba
遇到这样的情况,你需要从数据库中取某一条特定的数据,然后处理这条数据后存入另一个表,用foreach是吧,太慢了,换成Linq,上面草图中有个备注,再换成lamba试试。
5.数据放入内存
如果要从一个表中多次找数据来使用,那不如第一步先将这个表中所有数据或者需要的那部分特征数据都先放入内存中,从内存中读取的速度,大大的快于操作数据库,而这目的,也就是减少操作数据库的次数,耗性能。
6.使用BulkInsert
使用插件Extended,使用其中的BulkSaveChanges代替EF原生态的SaveChanges来保存数据,附截图来引用一段话:
然后我亲测结果如图:
1000条数据,BulkSaveChanges花费1s,SaveChanges花费27s……
插件名:
对于该插件的一些使用方法,我也附上网址:
http://www.zzzprojects.com/products/dotnet-development/bulk-operations/
7.使用SQL语句
如果你对速度还不满意,可以CRUD,直接使用SQL语句来操作。
可以参考:
http://my.oschina.net/Yamazaki/blog/185621
8.多表只需存一张
遇到这样的情况,P、T两张表,关系是1—–(0,1)的关系,你可能会先存入P表的数据,然后foreach P表的数据,找到对应的给T表的导航属性赋值,然后存入这条数据到T表,这个问题很严重也可笑,但是我身上缺犯了,只需要存 必须存导航数据(T表数据),有关联的表数据自然存入了(P表)
结尾
上述我总结的方法中,没有使用SQL语句,原先花费1小时20分钟的事,现在花费70s,竟然真的做到了。
EF性能优化的更多相关文章
- EF性能优化(一)
一:背景 说到EF的性能问题,我相信都是大家比较头痛的问题,有很多初学者望而却步,可是每每菜鸟在群里面抱怨EF太慢的时候,这个时候总有一些大牛登场说一句:怪EF咯?怪你不会用! 当然我从未嫌弃过它,因 ...
- [转]ASP.NET MVC3 + EF 性能优化解决方案以及最优架构
[集思广议] 我们用 asp.net mvc3 + ef 做了一个网站,现在是内测阶段,发现打开速度非常慢.首页打开(无缓存)都在5-6s以上(测试环境:程序和db都在本机),请问各位 mv ...
- EF性能优化-有人说EF性能低,我想说:EF确实不如ADO.NET
十年河东,十年河西,莫欺少年穷. EF就如同那个少年,ADO.NET则是一位壮年.毕竟ADO.NET出生在EF之前,而EF所走的路属于应用ADO.NET. 也就是说:你所写的LINQ查询,最后还是要转 ...
- EF性能优化篇一
https://www.cnblogs.com/chenwolong/p/7531955.html 1.合理使用AsNoTracking 若对查询的数据不需要做任何修改,则可采用AsNoTrackin ...
- C#中 EF 性能优化
https://www.cnblogs.com/chenwolong/p/7531955.html EF使用AsNoTracking(),无跟踪查询技术(查询出来的数据不可以修改,如果你做了修改,你会 ...
- C#实用杂记-EF全性能优化技巧
原文链接:http://www.makmong.com/947.html#comment-31 EntityFramework 优化建议 2016年1月15日 下午4:54 LEILINKANG ...
- 浅析Entity FrameWork性能优化
浅析EF性能优化 1. 数据Load 延迟加载:当实体第一次读取时,相关数据没有加载:当第一次试图访问导航属性时,所需的导航数据自动加载,EF默认使用这种方式加载数据,尽量使用预先加载和显 ...
- 一次EF批量插入多表数据的性能优化经历
距离上次的博客已经有15个多月了,感慨有些事情还是需要坚持,一旦停下来很有可能就会停很久或者从此再也不会坚持.但我个人一直还坚持认为属于技术狂热份子,且喜欢精益求精的那种.最近遇到两个和数据迁移相关的 ...
- EntityFramework之异步、事务及性能优化(九)
前言 本文开始前我将循序渐进先了解下实现EF中的异步,并将重点主要是放在EF中的事务以及性能优化上,希望通过此文能够帮助到你. 异步 既然是异步我们就得知道我们知道在什么情况下需要使用异步编程,当等待 ...
随机推荐
- weakSelf 运用 strongSelf来解决block的循环引用
SDWebImage 中有一段源码: #if SD_UIKIT Class UIApplicationClass = NSClassFromString(@"UIApplication&qu ...
- wpf(布局与Canvas )
WPF的布局有控件都在System.Windows.Controls.Panel这个基类下面,常见的布局控件: . canvas: Canvas是最基本的面板,它不会自动调整内部元素的排列及大小,它仅 ...
- 《Redis 持久化》
一:什么是持久化? - Redis 是内存级别的数据库.所谓持久化,即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)中. - 可以持久读取操作等的数据. - Redis 支持 R ...
- 20165225 《Java程序设计》第二周学习总结
20165225<Java程序设计>第二周学习总结 1.视频与课本中的学习: ##### 1.标识符: 字母.下划线.美元符号.数字(不能是true,false,null还有关键字). # ...
- jquery键盘事件
场景:当我们遇到onclick按钮事件时需要按回车执行时就用到了键盘监听事件 例如:<button id="sign_in_button" class="btn b ...
- 嵌入式Qt4.8.0支持ttf字库,并显示中文
引言 最近在做QT项目发现中文没法显示,于是百度QT嵌入式显示中文,基本上提示的都是把ttf字库转换为QPF(QT专门支持二进制的),发现这个qpf本身制作就很麻烦,按照网上的做法实实在在来了一边,发 ...
- (4.6)mysql备份还原——深入解析二进制日志(2)binlog参数配置解析
关键词:binlog配置,binlog参数,二进制日志配置,二进制文件参数配置 关键词:binlog缓存,binlog 刷新 0.bin写入流程 写binlog流程如下:# 数据操作buffer po ...
- MongoDB 新建数据库和集合 查询集合
MongoDB语法是原生ORM,根本不存在sql语句 创建数据库:这里和一般的关系型数据库一样,都要先建立一个自己的数据库空间 新建数据库db1 > use db1 switched to db ...
- redis缓存和mysql数据库同步
附redis关于缓存雪崩和缓存穿透,热点key 穿透 穿透:频繁查询一个不存在的数据,由于缓存不命中,每次都要查询持久层.从而失去缓存的意义. 解决办法: 持久层查询不到就缓存空结果,查询时先判断缓存 ...
- 帝国cms搜索关键字调用标签(showsearch)怎么用
前面ytkah介绍了如何让帝国CMS7.2搜索模板支持动态标签调用,现在我们来说说怎么调用帝国cms搜索关键字调用标签(showsearch).在帝国cms后台那边的使用方法:[showsearch] ...