Entity Framework——性能测试
内容提要
一、对EF框架的性能测试
增、删、改,查测试及性能优化
二、使用sql执行
增、删、改,查测试
三、对以上两种方式对比分析
一 对EF框架的测试
1插入操作测试
测试代码(关键部分)
List<Collection> list = new List<Collection>();
int i = 0;
while (i < count)
{
Collection cll = new Collection
{
Author = "test",
CitationNumber = 1,
DiscNo = "CCNDTEMP",
Downloads = 1,
FileName = "JSJJ20170803A030",
Period = "0",
PublicationDate = DateTime.Now,
PublicationName = "江苏经济报",
PublisherUnit = "江苏经济报",
ResourceType = "报纸",
TableName = "CAINTEMP",
Title = "无人驾驶汽车2020年将量产",
Year = "0"
};
list.Add(cll);
i++;
}
Stopwatch stw = new Stopwatch();
stw.Start();
db.Collections.AddRange(list);
stw.Stop();
var addTime = stw.ElapsedMilliseconds;
Stopwatch stwS = new Stopwatch();
stwS.Start();
db.SaveChanges();
stwS.Stop();
var saveTime = stwS.ElapsedMilliseconds;
Stopwatch stw = new Stopwatch();
stw.Start();
Parallel.ForEach(indexs, item =>
{
List<Collection> list = new List<Collection>();
int i = 0;
while (i < oneCounts)
{
Collection cll = new Collection
{
Author = "test" + item,
CitationNumber = 1,
DiscNo = "CCNDTEMP" + item,
Downloads = 1,
FileName = "JSJJ20170803A030" + item,
Period = "0",
PublicationDate = DateTime.Now,
PublicationName = "江苏经济报" + item,
PublisherUnit = "江苏经济报",
ResourceType = "报纸",
TableName = "CAINTEMP",
Title = "无人驾驶汽车2020年将量产",
Year = "0"
};
list.Add(cll);
i++;
}
using (CustomDbContext db = new CustomDbContext())
{
db.Collections.AddRange(list);
db.SaveChanges();
}
});
stw.Stop();
var saveTime = stw.ElapsedMilliseconds
数据统计
2查询测试
测试代码(关键部分)
Stopwatch stw = new Stopwatch();
stw.Start();
var count = db.Collections.Where(m => m.Author == "test2").OrderBy(m => m.Id).ToList();
stw.Stop();
var time = stw.ElapsedMilliseconds;
数据统计
3更新操作
测试代码(关键部分)
Stopwatch stw = new Stopwatch();
using (CustomDbContext db = new CustomDbContext())
{
var collection = db.Collections.Find(10000);
stw.Start();
collection.Author = "修改了作者";
db.Entry<Collection>(collection).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();
}
stw.Stop();
var time = stw.ElapsedMilliseconds;
Stopwatch stw = new Stopwatch();
using (CustomDbContext db = new CustomDbContext())
{
collections = db.Collections.Where(c => c.FileName == "JSJJ20170803A0301").ToList();
collections.RemoveRange(0, 199900);
stw.Start();
collections.ForEach(c =>
{
c.Author = "修改了作者y";
db.Entry<Collection>(c).State = System.Data.Entity.EntityState.Modified;
});
db.SaveChanges();
}
stw.Stop();
var time = stw.ElapsedMilliseconds;
数据统计
4删除
测试代码(关键部分)
Stopwatch stw = new Stopwatch();
stw.Start();
List<CollectionUser> collections = null;
using (CustomDbContext db = new CustomDbContext())
{
collections = db.CollectionUsers.Where(c => c.Collection.FileName == "JSJJ20170803A0301").ToList();
collections.RemoveRange(0, 2713);
db.CollectionUsers.RemoveRange(collections);
db.SaveChanges();
}
stw.Stop();
var time = stw.ElapsedMilliseconds;
数据统计
5针对各种优化方案的测试
贪婪加载与延迟加载
开启延迟加载要满足两个条件:
1)在定时实体时,使用virtual,public or protected修饰实体的导航属性,不能使用sealed修饰。
2)使用默认的DbContextConfiguration.LazyLoadingEnabled配置,或将其设置为true
3)使用默认的DbContextConfiguration.ProxyCreationEnabled配置,或将其设置为true
若不满足上述两个条件则为贪婪加载
查询数据统计:
加载类型及说明 |
数据量 |
耗时(ms) |
---|---|---|
贪婪加载(未使用导航属性) |
4003 |
2128 |
2120 | ||
2181 | ||
延迟加载(未使用导航属性) |
2102 |
|
2327 | ||
2064 | ||
延迟加载(使用导航属性) |
4003(关联导航属性在20000+) |
>10s |
分析
在数据量小的情况下,两种数据加载模式耗时基本相同,但当数据量较大,例如本次试验中关联导航属性记录数在2万以上时,延迟加载模式耗时巨大,因此适当关闭延迟加载可提高性能;延迟加载可以实现按需获取数据,这样客户端与服务端的传输数据量有可能减小,且也会相应地减少服务器端的内存消耗。
使用AsNoTracking()
查询数据统计
说明 |
检索条件 |
耗时 |
---|---|---|
200万的数据表 |
Where(m => m.Author ==test2).OrderBy(m =>m.Id).ToList() |
9440 |
7232 | ||
9086 | ||
7435 | ||
7637 |
分析
使用AsNoTracking()第一次查询较慢,第二次比不使用AsNoTracking()快一点,不过性能提高不大。
设置IsUnicode
IsUnicode(false)则在code first模式下,string类型实体字段对应着varchar类型的表字段,
若不配置或IsUnicode(true),则对应着text类型的。
IsUnicode设置 |
检索条件 |
表字段类型 |
耗时(ms) |
---|---|---|---|
true |
AsNoTracking(),Queryable.Where(m => m.Author ==test2).OrderBy(m =>m.Id).ToList() |
varchar |
8407 |
10952 | |||
8528 | |||
8674 | |||
10492 | |||
11685 | |||
7659 |
分析
对于EF6来说,是否使用IsUnicode对查询速度基本没有影响。之前的版本会产生类型转换的问题,但实测来看EF6不会。
二 使用sql和MySql.Data.dll
1 添加
测试代码(关键部分)
Stopwatch stw = new Stopwatch();
stw.Start();
int loop = 10000;
Collection cll = new Collection
{
Author = "test",
CitationNumber = 1,
DiscNo = "CCNDTEMP",
Downloads = 1,
FileName = "JSJJ20170803A030",
Period = "0",
PublicationDate = DateTime.Now,
PublicationName = "江苏经济报",
PublisherUnit = "江苏经济报",
ResourceType = "报纸",
TableName = "CAINTEMP",
Title = "无人驾驶汽车2020年将量产",
Year = "0"
};
string values = "(@FileName0, @Title0, @TableName0, @DiscNo0, @ResourceType0, @Downloads0, @CitationNumber0, @Author0, @PublicationName0, @PublisherUnit0, @PublicationDate0, @Year0, @Period0)";
Parameters param = new Parameters();
for (int i = 0; i < loop; i++)
{
if (i < loop-1)
{
values = values + "," + string.Format("(@FileName{0}, @Title{0}, @TableName{0}, @DiscNo{0}, @ResourceType{0}, @Downloads{0}, @CitationNumber{0}, @Author{0}, @PublicationName{0}, @PublisherUnit{0}, @PublicationDate{0}, @Year{0}, @Period{0})"
, i+1);
}
param.AddParameter("@FileName"+i, MySqlDbType.VarChar, 50, cll.FileName);
param.AddParameter("@Title" + i, MySqlDbType.VarChar, 200, cll.Title);
param.AddParameter("@TableName" + i, MySqlDbType.VarChar, 50, cll.TableName);
param.AddParameter("@DiscNo" + i, MySqlDbType.VarChar, 50, cll.DiscNo);
param.AddParameter("@ResourceType" + i, MySqlDbType.VarChar, 50, cll.ResourceType);
param.AddParameter("@Downloads" + i, MySqlDbType.Int32, 11, cll.Downloads);
param.AddParameter("@CitationNumber" + i, MySqlDbType.Int32, 11, cll.CitationNumber);
param.AddParameter("@Author" + i, MySqlDbType.VarChar, 50, cll.Author);
param.AddParameter("@PublicationName" + i, MySqlDbType.VarChar, 200, cll.PublicationName);
param.AddParameter("@PublisherUnit" + i, MySqlDbType.VarChar, 200, cll.PublisherUnit);
param.AddParameter("@PublicationDate" + i, MySqlDbType.DateTime, 50, cll.PublicationDate);
param.AddParameter("@Year" + i, MySqlDbType.VarChar, 10, cll.Year);
param.AddParameter("@Period" + i, MySqlDbType.VarChar, 10, cll.Period);
}
string sql = @"INSERT INTO collections (`FileName`, `Title`, `TableName`, `DiscNo`, `ResourceType`, `Downloads`, `CitationNumber`, `Author`, `PublicationName`, `PublisherUnit`, `PublicationDate`, `Year`, `Period`)
VALUES"+values;
stw.Stop();
var addTime = stw.ElapsedMilliseconds;
Stopwatch stwS = new Stopwatch();
stwS.Start();
MySqlService service = new MySqlService("database=noef_testdb;server=192.168.107.13;uid=root;pwd=cnki2017;port=3306;Character Set=utf8;");
service.ExecuteNonQuery(sql, param);
stwS.Stop();
var saveTime = stwS.ElapsedMilliseconds;
数据统计
2 读取
测试代码(关键部分)
Stopwatch stwS = new Stopwatch();
stwS.Start();
string sql = @"select * from collections where Author ='test2' order by Id DESC";
MySqlService service = new MySqlService("database=ef_testdb;server=192.168.107.13;uid=root;pwd=cnki2017;port=3306;Character Set=utf8;");
service.ExecuteReader(sql);
stwS.Stop();
var saveTime = stwS.ElapsedMilliseconds;
数据统计
单表(已有200万数据),单条查找 |
查找条件 |
耗时(ms) |
---|---|---|
Id =100000 |
170 |
|
159 | ||
158 | ||
单表(已有200万数据),查找多条 |
FileName ='JSJJ20170803A0301' |
5403 |
4247 | ||
3613 | ||
FileName ='JSJJ20170803A0301' order by Id DESC |
10904 |
|
8941 | ||
7265 | ||
6633 | ||
7048 | ||
Author ==test2 order by Id DESC |
12958 |
|
8619 | ||
8481 | ||
8030 |
3 更新
测试代码(关键部分)
Stopwatch stwS = new Stopwatch();
stwS.Start();
string sql = "UPDATE collections SET Author = '不使用EF' WHERE Id =10000";
MySqlService service = new MySqlService("database=ef_testdb;server=192.168.107.13;uid=root;pwd=cnki2017;port=3306;Character Set=utf8;");
service.ExecuteNonQuery(sql);
stwS.Stop();
var saveTime = stwS.ElapsedMilliseconds;
数据统计
单表(已有200万数据) |
sql |
耗时(ms) |
---|---|---|
(多条跟新)UPDATE SET Author = '不使用EF' WHERE FileName ='JSJJ20170803A0301' |
229 |
|
171 | ||
172 | ||
(单条更新)UPDATE SET Author = '不使用EF' WHERE Id =10000 |
307 |
|
194 | ||
218 | ||
197 |
4 删除
测试代码(关键部分)
Stopwatch stwS = new Stopwatch();
stwS.Start();
string sql = @"delete `collectionusers` from `collectionusers`,`collections` where `collections`.`Id` = `collectionusers`.`Collection_Id` and collections.FileName ='JSJJ20170803A0301'";
MySqlService service = new MySqlService("database=ef_testdb;server=192.168.107.13;uid=root;pwd=cnki2017;port=3306;Character Set=utf8;");
service.ExecuteNonQuery(sql);
stwS.Stop();
var saveTime = stwS.ElapsedMilliseconds;
数据统计
单表(已有6万数据) |
sql |
耗时(ms) |
---|---|---|
delete from collectionusers where Id = 320 |
198 |
|
175 | ||
221 | ||
(单条更新)UPDATE SET Author = '不使用EF' WHERE Id =10000 |
307 |
|
194 | ||
218 | ||
197 | ||
UPDATE SET Author = '不使用EF' WHERE Id =10000(未找到,而未删除成功) |
195 |
|
194 | ||
202 | ||
(删除2000+条记录)delete `collectionusers` from `collectionusers`,`collections` where `collections`.`Id` = `collectionusers`.`Collection_Id` and collections.FileName ='JSJJ20170803A0301' |
370 |
三 对比分析
测试环境:
两台机器A和B,A是测试程序运行机器,B是Mysql运行机器,A和B在局域网内。
A
B
AB及网络对结果的影响:
AB机器之间的网络通信耗费一定的时间,但局域网内一般很小,且不单纯看执行时间,单纯看执行时间意义不大,本测试目的是通过比较研究EF框架的性能,另外实际的系统部署中,也不会将应用与数据库部署到同一台机器。
每中操作执行3~6次左右,如果发现某次执行时间过长或过短会多执行几次,严格来讲,只有统计数据的数量达到一定程度才能得出比较接近事实的结论,但这里在满足一定条件的前提下,例如:保持网络状态良好,保持机器运行良好,保证测试程序正确,在这样的前提下减少测试次数也可以得出比较接近事实的结论;在统计分析中没有将所有数据加一对比,也没有采用取平均值等方式,因为只是想从数量级上来加以对比。
1 添加操作
数据量 |
使用EF框架 |
Sql+MySql.Data.dll(简写NOEF) |
结论 |
说明 |
---|---|---|---|---|
1000 |
741+2141 |
93+235 |
大致相差一个数量级 |
空表,单线程 |
10000 |
826+14930 |
14521+851 |
大致相等 |
分析 插入数据量是1000时相差了一个数量级,而数据量为10000为花费时间大致相等,由统计数据可见耗时主要是对待插入数据的处理,实际的数据库操作还是相当快的,所以在实际应用过程中,如果代码实现的不好,那么可能比使用EF框架的读写性能还差,好在对待插入数据的处理优化比较容易。
数据量 |
使用EF框架 |
Sql+MySql.Data.dll(简写NOEF) |
结论 |
说明 |
---|---|---|---|---|
10000 |
6322 |
14521+851 |
大致相差一个数量级,但实际使用不会这么大 |
空表,EF框架10线程,最大并发数2; NoEF单线程 |
分析
使用EF框架同时使用多线程改进插入速度,并发数为2时,性能大致提升一倍;相比NoEF单线程而言性能已相差无几,当然,并不是任何时候都可以使用多线程来提高读写速度。
数据量 |
使用EF框架 |
Sql+MySql.Data.dll(简写NOEF) |
结论 |
说明 |
---|---|---|---|---|
100000 |
28982 |
15066(一次)+2665 |
大致相差一个数量级,但实际使用不会这么大 |
表已有数据80万,10线程,最大并发数2; |
分析
两种方式都是都是10线程,数据插入速度大致相差一个数量级,考虑到NOEF方式下要处理数据的问题,那么性能相差就没有这么大了,其实实际的应用也与这种情况是相似的。
2 查找
数据量 |
使用EF框架 |
Sql+MySql.Data.dll(简写NOEF) |
结论 |
说明 |
---|---|---|---|---|
一条 |
1669 |
170 |
单纯的多条查找性能基本相同, |
表已有200万数据,检索条件相同 |
多条 |
7823 |
5403 |
||
719 |
12958 |
检索条件相同,但使用ToList() |
分析
当检索一条时并且使用Id值,检索速度相差一个数量级;而查找多条时,性能基本相同,然而会发现一个奇怪的现象,就是使用EF对检索结果ToList()与不转换,耗时相差较大。
3 更新
数据量 |
使用EF框架 |
Sql+MySql.Data.dll(简写NOEF) |
结论 |
说明 |
---|---|---|---|---|
一条 |
112 |
307 |
总体上EF更新性能比NOEF查得多 |
表已有200万数据 |
多条 |
407203 |
229 |
分析
更新一条数据EF反而比NOEF要快,但是相差也不多,可以判定性能基本一致;当更新多条时,NOEF性能明显比EF框架好,相差几个数量级。
4 删除
数据量 |
使用EF框架 |
Sql+MySql.Data.dll(简写NOEF) |
结论 |
说明 |
---|---|---|---|---|
一条 |
2080 |
221 |
删除操作EF耗时与NOEF相差一个数量级,然而多条操作 |
表已有6万数据 删除多条时,NOEF方式下一次删除2000+条记录,而EF方式下删除500条记录 |
多条 |
407203 |
370 |
分析
从NOEF方式下一次删除2000+条记录,而EF方式下删除500条记录这一结果来看,NOEF性能明显优于EF,且NOEF方式下,删除操作耗时随删除数据量平稳增长且增长率很小;但EF操作耗时随操作数据量增大而明显增大;另外,当NOEF方式下,没有找到数据而不能删除数据时,耗时202左右,也就是说数据量很小时,譬如删除几条或十几条操作时间基本等同于查询时间。
-----------------------------------------------------------------------------------------
时间仓促,水平有限,如有不当之处,欢迎指正。
- mybatis 对于基本类型数据传值的问题
- Guava------------Cache使用方法
- hadoop安装及配置入门篇
- 剖析Go编写的Socket服务器模块解耦及基础模块的设计
- hihocoder-平衡树·SBT
- mysql 查询数据库表结构
- Go语言基于Socket编写服务器端与客户端通信的实例
- 前端游戏编程基础-如何实现Canvas图像的拖拽、点击等操作
- 使用Go开发一个简单的服务器程序
- Golang URL解析
- golang实现基于redis和consul的可水平扩展的排行榜服务范例
- golang学习之旅:使用go语言操作mysql数据库
- golang微信支付服务端
- flume安装及配置介绍(二)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法