内容提要

一、对EF框架的性能测试

增、删、改,查测试及性能优化

二、使用sql执行

增、删、改,查测试

三、对以上两种方式对比分析

一 对EF框架的测试

1插入操作测试

测试代码(关键部分)

List<Collection> list = new List<Collection>();
int i = ;
while (i < count)
{
Collection cll = new Collection
{
Author = "test",
CitationNumber = ,
DiscNo = "CCNDTEMP",
Downloads = ,
FileName = "JSJJ20170803A030",
Period = "",
PublicationDate = DateTime.Now,
PublicationName = "江苏经济报",
PublisherUnit = "江苏经济报",
ResourceType = "报纸",
TableName = "CAINTEMP",
Title = "无人驾驶汽车2020年将量产",
Year = ""
};
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 = ;
while (i < oneCounts)
{
Collection cll = new Collection
{
Author = "test" + item,
CitationNumber = ,
DiscNo = "CCNDTEMP" + item,
Downloads = ,
FileName = "JSJJ20170803A030" + item,
Period = "",
PublicationDate = DateTime.Now,
PublicationName = "江苏经济报" + item,
PublisherUnit = "江苏经济报",
ResourceType = "报纸",
TableName = "CAINTEMP",
Title = "无人驾驶汽车2020年将量产",
Year = ""
};
list.Add(cll);
i++;
}
using (CustomDbContext db = new CustomDbContext())
{
db.Collections.AddRange(list);
db.SaveChanges();
}
});
stw.Stop();
var saveTime = stw.ElapsedMilliseconds

数据统计

单表(空表)单线程

插入数量(条)

AddRange(ms)

SaveChanges(ms)

1000

741

2141

752

1672

742

1873

745

2145

757

1513

781

2732

10000

835

12797

826

14930

832

13421

835

11522

842

13963

832

11265

100000

4127

144663

3132

137083

1804

121466

单表(空表)10线程(最大并发数2),每个线程操作100条数据

1000

未计算

2102

1845

1992

2017

2007

单表(空表)10线程(最大并发数2),每个线程操作1000条数据

10000

未计算

6322

5480

5285

单表(空表)10线程(最大并发数2),每个线程操作10000条数据

100000

30071

28466

30113

单表(已有20万数据)10线程(最大并发数2),每个线程操作10000条数据

28638

单表(已有40万数据)10线程(最大并发数2),每个线程操作10000条数据

28932

单表(已有80万数据)10线程(最大并发数2),每个线程操作10000条数据

28982

单表(已有100万数据)10线程(最大并发数2),每个线程操作10000条数据

31049

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;

数据统计

单表(已有200万数据),单条查找

查找说明

耗时(ms)

执行DbSet<TEntity>.Find,查第100条

1728

1665

DbSet<TEntity>.Find,查第10000条

1668

1686

DbSet<TEntity>.Find,查第100000条

1664

1677

Queryable.FirstOrDefault(m => m.TableName ==CAINTEMP1)

4951

4759

4771

Queryable.Where(m => m.Author ==test2)

712

719

696

Queryable.Where(m => m.Author ==test2).Count()

3921

3917

2957

3919

3734

Queryable.Where(m => m.Author ==test2).ToList()

6841

7188

6907

7351

7335

7300

Queryable.Where(m => m.Author ==test2).OrderBy(m =>m.Id)

719

711

706

Queryable.Where(m => m.Author ==test2).OrderBy(m =>m.Id).ToList()

7823

7290

7385

3更新操作

测试代码(关键部分)

Stopwatch stw = new Stopwatch();

            using (CustomDbContext db = new CustomDbContext())

            {

                var collection = db.Collections.Find();

                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(, );

                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;

数据统计

 

单表(已有200万数据)

操作

耗时(ms)

更新一条

112

115

113

更新100条

42140

42520

更新1000条

407203

424386

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(, );

                db.CollectionUsers.RemoveRange(collections);

                db.SaveChanges();

            }

            stw.Stop();

            var time = stw.ElapsedMilliseconds;

数据统计

单表(已有6万数据)

操作

耗时(ms)

删除1条记录

2080

1993

1926

删除100条

2824

3385

2480

删除400条

3005

删除500条

3526

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 = ;

            Collection cll = new Collection

            {

                Author = "test",

                CitationNumber = ,

                DiscNo = "CCNDTEMP",

                Downloads = ,

                FileName = "JSJJ20170803A030",

                Period = "",

                PublicationDate = DateTime.Now,

                PublicationName = "江苏经济报",

                PublisherUnit = "江苏经济报",

                ResourceType = "报纸",

                TableName = "CAINTEMP",

                Title = "无人驾驶汽车2020年将量产",

                Year = ""

            };

            string values = "(@FileName0, @Title0, @TableName0, @DiscNo0, @ResourceType0, @Downloads0, @CitationNumber0, @Author0, @PublicationName0, @PublisherUnit0, @PublicationDate0, @Year0, @Period0)";

            Parameters param = new Parameters();

            for (int i = ; i < loop; i++)

            {

                if (i < loop-)

                {

                    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+);

                }      

                param.AddParameter("@FileName"+i, MySqlDbType.VarChar, , cll.FileName);

                param.AddParameter("@Title" + i, MySqlDbType.VarChar, , cll.Title);

                param.AddParameter("@TableName" + i, MySqlDbType.VarChar, , cll.TableName);

                param.AddParameter("@DiscNo" + i, MySqlDbType.VarChar, , cll.DiscNo);

                param.AddParameter("@ResourceType" + i, MySqlDbType.VarChar, , cll.ResourceType);

                param.AddParameter("@Downloads" + i, MySqlDbType.Int32, , cll.Downloads);

                param.AddParameter("@CitationNumber" + i, MySqlDbType.Int32, , cll.CitationNumber);

                param.AddParameter("@Author" + i, MySqlDbType.VarChar, , cll.Author);

                param.AddParameter("@PublicationName" + i, MySqlDbType.VarChar, , cll.PublicationName);

                param.AddParameter("@PublisherUnit" + i, MySqlDbType.VarChar, , cll.PublisherUnit);

                param.AddParameter("@PublicationDate" + i, MySqlDbType.DateTime, , cll.PublicationDate);

                param.AddParameter("@Year" + i, MySqlDbType.VarChar, , cll.Year);

                param.AddParameter("@Period" + i, MySqlDbType.VarChar, , 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;

数据统计

单表(空表)单线程

插入数量(条)

数据拼接(ms)

INSERT ExecuteNonQuery(ms)

1000

93

235

94

227

92

229

10000

14521

851

14695

851

14825

857

单表(空表)10线程(最大并发数2),每个线程操作1000条数据

10000

14762(仅拼接一次的时间,即10000条)

2870

2866

2682

单表(已有50万数据)10线程(最大并发数2),每个线程操作1000条数据

14808(仅拼接一次的时间,即10000条)

2643

单表(已有80万数据)10线程(最大并发数2),每个线程操作1000条数据

15066(仅拼接一次的时间,即10000条)

2665

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左右,也就是说数据量很小时,譬如删除几条或十几条操作时间基本等同于查询时间。

-----------------------------------------------------------------------------------------

转载与引用请注明出处。

时间仓促,水平有限,如有不当之处,欢迎指正。

Entity Framework——性能测试的更多相关文章

  1. Entity Framework与ADO.NET批量插入数据性能测试

    Entity Framework是.NET平台下的一种简单易用的ORM框架,它既便于Domain Model和持久层的OO设计,也提高了代码的可维护性.但在使用中发现,有几类业务场景是EF不太擅长的, ...

  2. 一个Entity Framework、ADO.NET查询性能测试

    Entity Framework自然是会比ADO.NET性能慢点,这个不多说了.直接上结果. 本该用测试项目的,不过我建了个aspx.下面是随便测20遍得到的结果 补充!!把12行改成 list = ...

  3. Entity Framework Code First+SQL Server,改变聚集索引,提高查询性能

    .net Entity Framework(调研的是Entity Framework 4.0) code first方式生成数据库时,不能修改数据库表的索引,而SQLServer默认会把数据表的主键设 ...

  4. ORM系列之Entity FrameWork详解

    一. 谈情怀 从第一次接触开发到现在(2018年),大约有六年时间了,最初阶段连接数据库,使用的是[SQL语句+ADO.NET],那时候,什么存储过程.什么事务 统统不理解,生硬的将SQL语句传入SQ ...

  5. 细说ORM之Entity FrameWork系列(被替换)

    一. 谈情怀 从第一次接触开发到现在(2018年),接近五年时间了,最初阶段连接数据库,使用的是[SQL语句+ADO.NET],那时候,什么存储过程.什么事务 统统不理解,生硬的将SQL语句传入SQL ...

  6. ASP.NET MVC深入浅出系列(持续更新) ORM系列之Entity FrameWork详解(持续更新) 第十六节:语法总结(3)(C#6.0和C#7.0新语法) 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字 各种通讯连接方式 设计模式篇 第十二节: 总结Quartz.Net几种部署模式(IIS、Exe、服务部署【借

    ASP.NET MVC深入浅出系列(持续更新)   一. ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态模 ...

  7. ASP.NET MVC with Entity Framework and CSS一书翻译系列文章之第二章:利用模型类创建视图、控制器和数据库

    在这一章中,我们将直接进入项目,并且为产品和分类添加一些基本的模型类.我们将在Entity Framework的代码优先模式下,利用这些模型类创建一个数据库.我们还将学习如何在代码中创建数据库上下文类 ...

  8. Entity Framework Core 1.1 升级通告

    原文地址:https://blogs.msdn.microsoft.com/dotnet/2016/11/16/announcing-entity-framework-core-1-1/ 翻译:杨晓东 ...

  9. Entity Framework Core 实现MySQL 的TimeStamp/RowVersion 并发控制

    将通用的序列号生成器库 从SQL Server迁移到Mysql 遇到的一个问题,就是TimeStamp/RowVersion并发控制类型在非Microsoft SQL Server数据库中的实现.SQ ...

随机推荐

  1. Dell解决黑苹果网卡(BCM94352ZAE/DW1560)怎么都打不开WiFi

    Dell解决黑苹果网卡(BCM94352ZAE/DW1560)怎么都打不开WiFi 2017年10月20日17:41:00 by SemiconductorKING 本来觉得驱动这个网卡不是个问题,以 ...

  2. 移动端适配(3)——rem适配

    rem适配 <meta name="viewport"  content="width=device-width,user-scalable=no"/&g ...

  3. WHILE (Transact-SQL)

    ---循环 declare @n int declare @rowcount int declare @name varchar(50) create table #temp ( id int ide ...

  4. 工作中常用的sql语句以及知识整理

    一.常用的sql语句 1.建表语句 create table tabname(colname1 type1 [not null][primary key], colname2 type2,...) 根 ...

  5. Angular-ui/bootstarp modal 主控制器与模态框控制器传值

    调用模态框: $scope.open = function (size) { //这里很关键,是打开模态框的过程 var modalInstance = $uibModal.open({ animat ...

  6. Linux基础之-元字符

    Bash中的特殊字符,键盘上能敲出来的特殊字符都有其特殊意义,强调一点:元字符是被shell解释的. 1. '',取命令的执行结果 [root@MiWiFi-R3-srv ~]# ls4.txt an ...

  7. OpenLayers中的图层(转载)

    作者:田念明出处:http://www.cnblogs.com/nianming/本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法 ...

  8. 注册表----修改Win7登录界面

    在进行操作前,需要准备好背景图片.对背景图片的要求有三点: (1)图片必须是JPG格式: (2)必须将图片命名为backgroundDefault; (3)图片的体积必须小于256KB. 按下[Win ...

  9. odps编写UDF的实现

    问题 尝试写一个UDF,参数支持输入x,y与一个Polygon,返回结果是(x,y)是否在输入的Geometry之内? 环境 eclipse odps 插件 jts包:jts-1.8.jar 解法 i ...

  10. Spring3实战第二章第二小节 IOC依赖注入 list和map集合

    Spring有多种依赖注入的形式,本篇文章仅介绍Spring通过xml进行IOC配置的方式. 1.Set注入 2.构造器注入 平常的Java开发中,程序员在某个类中需要依赖其它类的方法. 通常是new ...