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

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

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏逸鹏说道

程序猿是如何解决SQLServer占CPU100%的

文章目录 遇到的问题 使用SQLServer Profiler监控数据库 SQL1:查找最新的30条告警事件 SQL2:获取当前的总报警记录数 有哪些SQL语句...

3068
来自专栏杨建荣的学习笔记

关于评审开发人员的sql语句(r3笔记第11天)

在平时的工作中,经常会有一些开发人员提出一些数据库相关的一些问题。可能问的最多的就是sql语句了。 按照一个标准的流程,开发提交的sql语句在完成一系列测试之后...

3657
来自专栏Netkiller

数据库恢复方案

数据库恢复方案 摘要 这里所谈的内容是对备份数据的恢复,不是对损坏数据表的恢复,或者说灾难恢复。 目录 1. 背景 2. 备份方式分析 3. 恢复方案 3.1....

2715
来自专栏Netkiller

数据库恢复方案

数据库恢复方案 摘要 这里所谈的内容是对备份数据的恢复,不是对损坏数据表的恢复,或者说灾难恢复。 目录 1. 背景 2. 备份方式分析 3. 恢复方案 3.1....

2575
来自专栏数据和云

巧用复合索引,有效降低系统IO

我们知道索引至关重要,合理的索引使用能够在很大程度上改善数据库的性能。然而很多人都会走入这样一个误区:走索引的SQL语句的性能一定比全表扫描好。真的是这样吗?今...

2779
来自专栏杨建荣的学习笔记

Oracle中的段(r10笔记第81天)

Oracle的体系结构中,关于存储结构大家应该都很熟悉了。 估计下面这张图大家都看得熟悉的不能再熟悉了。 ? 简单来说,里面的一个重要概念就是段,如果是开发...

3388
来自专栏乐沙弥的世界

Oracle 表空间与数据文件

SYSAUX --->10g 高并发系统繁忙时,会造成system争用,将工具放到SYSAUX,减轻system的压力,SYSAUX不影响系统(影响性能)

795
来自专栏大内老A

如何追踪每一笔记录的来龙去脉:一个完整的Audit Logging解决方案[上篇]

一、提出问题 在开发一个企业级 应用的时候,尤其在一个涉及到敏感数据的应用,比如财务系统、物流系统,我们往往有这样的需求:对于数据库中每一笔数据的添加、修改和删...

2049
来自专栏Netkiller

数据库恢复方案

目录 1. 背景 2. 备份方式分析 3. 恢复方案 3.1. 第一种 3.2. 第二种 3.3. 第三种 3.4. 第四种 4. 手工恢复 1. 背景 我们来...

2728
来自专栏腾讯Bugly的专栏

移动客户端中高效使用 SQLite

导语 iOS 程序能从网络获取数据。少量的 KV 类型数据可以直接写文件保存在 Disk 上,App 内部通过读写接口获取数据。稍微复杂一点的数据类型,也可以将...

3456

扫描关注云+社区