内容提要
一、对EF框架的性能测试
增、删、改,查测试及性能优化
二、使用sql执行
增、删、改,查测试
三、对以上两种方式对比分析
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
数据统计
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;
数据统计
开启延迟加载要满足两个条件:
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万以上时,延迟加载模式耗时巨大,因此适当关闭延迟加载可提高性能;延迟加载可以实现按需获取数据,这样客户端与服务端的传输数据量有可能减小,且也会相应地减少服务器端的内存消耗。
查询数据统计
说明 | 检索条件 | 耗时 |
---|---|---|
200万的数据表 | Where(m => m.Author ==test2).OrderBy(m =>m.Id).ToList() | 9440 |
7232 | ||
9086 | ||
7435 | ||
7637 |
分析
使用AsNoTracking()第一次查询较慢,第二次比不使用AsNoTracking()快一点,不过性能提高不大。
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不会。
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 |
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 |
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次左右,如果发现某次执行时间过长或过短会多执行几次,严格来讲,只有统计数据的数量达到一定程度才能得出比较接近事实的结论,但这里在满足一定条件的前提下,例如:保持网络状态良好,保持机器运行良好,保证测试程序正确,在这样的前提下减少测试次数也可以得出比较接近事实的结论;在统计分析中没有将所有数据加一对比,也没有采用取平均值等方式,因为只是想从数量级上来加以对比。
数据量 | 使用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方式下要处理数据的问题,那么性能相差就没有这么大了,其实实际的应用也与这种情况是相似的。
数据量 | 使用EF框架 | Sql+MySql.Data.dll(简写NOEF) | 结论 | 说明 |
---|---|---|---|---|
一条 | 1669 | 170 | 单纯的多条查找性能基本相同, | 表已有200万数据,检索条件相同 |
多条 | 7823 | 5403 | ||
719 | 12958 | 检索条件相同,但使用ToList() |
分析
当检索一条时并且使用Id值,检索速度相差一个数量级;而查找多条时,性能基本相同,然而会发现一个奇怪的现象,就是使用EF对检索结果ToList()与不转换,耗时相差较大。
数据量 | 使用EF框架 | Sql+MySql.Data.dll(简写NOEF) | 结论 | 说明 |
---|---|---|---|---|
一条 | 112 | 307 | 总体上EF更新性能比NOEF查得多 | 表已有200万数据 |
多条 | 407203 | 229 |
分析
更新一条数据EF反而比NOEF要快,但是相差也不多,可以判定性能基本一致;当更新多条时,NOEF性能明显比EF框架好,相差几个数量级。
数据量 | 使用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左右,也就是说数据量很小时,譬如删除几条或十几条操作时间基本等同于查询时间。
-----------------------------------------------------------------------------------------
时间仓促,水平有限,如有不当之处,欢迎指正。