Entity Framework——性能测试

时间:2022-05-03
本文章向大家介绍Entity Framework——性能测试,主要内容包括一 对EF框架的测试、2查询测试、5针对各种优化方案的测试、二 使用sql和MySql.Data.dll、3 更新、4 删除、三 对比分析、2 查找、3 更新、4 删除、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

内容提要

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

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

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