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

数据统计

单表(空表)单线程

插入数量(条)

AddRangems

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

 

数据统计

 

单表(已有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(0, 2713);

                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)在定时实体时,使用virtualpublic 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

IsUnicodefalse)则在code first模式下,string类型实体字段对应着varchar类型的表字段,

若不配置或IsUnicodetrue),则对应着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不会。

使用sqlMySql.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;

数据统计

单表(空表)单线程

插入数量(条)

数据拼接(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

对比分析

测试环境:

两台机器ABA是测试程序运行机器,BMysql运行机器,AB在局域网内。

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

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

转载与引用请注明出处。

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

原文地址:https://www.cnblogs.com/hdwgxz/p/7860077.html