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

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

    转载与引用请注明出处。

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

  • 相关阅读:
    PHP面试:实现动态获取函数参数的方法
    PHP面试:什么是类的多态性,请写出一个例子
    php相关操作
    客户端app支付宝登录接口
    商品分类设计
    Git连接远程服务器
    iptables/mysql设置指定主机访问指定端口
    CMake安装grpc生成gRPCTargets.cmake文件
    Linux下Springboot解决`APR based Apache Tomcat Native library`提示
    java双重检测或枚举类实现线程安全单例(懒汉模式)
  • 原文地址:https://www.cnblogs.com/hdwgxz/p/7860077.html
Copyright © 2011-2022 走看看