zoukankan      html  css  js  c++  java
  • Dapper, Ef core, Freesql 插入大量数据性能比较(二)

    上一篇文章中,我们比较出单表插入9999行数据,Freesql > Dapper > EfCore。在本文中,我们来看看级联插入

    构建9999行数据

    List<Entity> datas = new List<Entity>();
    for (int i = 0; i < 9999; i++)
    {
        var item = new Entity
        {
            No = i + 1,
            Col1 = Guid.NewGuid().ToString("N"),
            Col2 = Guid.NewGuid().ToString("N"),
            Col3 = Guid.NewGuid().ToString("N"),
            Col4 = Guid.NewGuid().ToString("N"),
            Col5 = Guid.NewGuid().ToString("N"),
            Col6 = Guid.NewGuid().ToString("N"),
            Col7 = Guid.NewGuid().ToString("N"),
            Col8 = Guid.NewGuid().ToString("N"),
            Col9 = Guid.NewGuid().ToString("N"),
            Col10 = Guid.NewGuid().ToString("N"),
        };
        item.EntitySubs.Add(new EntitySub
        {
            Col1 = Guid.NewGuid().ToString("N"),
            Col2 = Guid.NewGuid().ToString("N"),
            Col3 = Guid.NewGuid().ToString("N"),
            Col4 = Guid.NewGuid().ToString("N"),
            Col5 = Guid.NewGuid().ToString("N"),
            Col6 = Guid.NewGuid().ToString("N"),
            Col7 = Guid.NewGuid().ToString("N"),
            Col8 = Guid.NewGuid().ToString("N"),
            Col9 = Guid.NewGuid().ToString("N"),
            Col10 = Guid.NewGuid().ToString("N"),
        });
        item.EntitySubs.Add(new EntitySub
        {
            Col1 = Guid.NewGuid().ToString("N"),
            Col2 = Guid.NewGuid().ToString("N"),
            Col3 = Guid.NewGuid().ToString("N"),
            Col4 = Guid.NewGuid().ToString("N"),
            Col5 = Guid.NewGuid().ToString("N"),
            Col6 = Guid.NewGuid().ToString("N"),
            Col7 = Guid.NewGuid().ToString("N"),
            Col8 = Guid.NewGuid().ToString("N"),
            Col9 = Guid.NewGuid().ToString("N"),
            Col10 = Guid.NewGuid().ToString("N"),
        });
        item.EntitySubs.Add(new EntitySub
        {
            Col1 = Guid.NewGuid().ToString("N"),
            Col2 = Guid.NewGuid().ToString("N"),
            Col3 = Guid.NewGuid().ToString("N"),
            Col4 = Guid.NewGuid().ToString("N"),
            Col5 = Guid.NewGuid().ToString("N"),
            Col6 = Guid.NewGuid().ToString("N"),
            Col7 = Guid.NewGuid().ToString("N"),
            Col8 = Guid.NewGuid().ToString("N"),
            Col9 = Guid.NewGuid().ToString("N"),
            Col10 = Guid.NewGuid().ToString("N"),
        });
        datas.Add(item);
    }

    Dapper:

    static void AddDataByDapperCascade(List<Entity> datas)
    {
        #region 数据格式转换
        var dataTemporarys = new List<EntityTemporary>();
        var dataSubTemporarys = new List<EntitySubTemporary>();
    
        for (int i = 0, length = datas.Count; i < length; i++)
        {
            var item = datas[i];
            var newItem = new EntityTemporary
            {
                No = item.No,
                Col1 = item.Col1,
                Col2 = item.Col2,
                Col3 = item.Col3,
                Col4 = item.Col4,
                Col5 = item.Col5,
                Col6 = item.Col6,
                Col7 = item.Col7,
                Col8 = item.Col8,
                Col9 = item.Col9,
                Col10 = item.Col10,
                Position = i + 1
            };
            dataTemporarys.Add(newItem);
            dataSubTemporarys.AddRange(item.EntitySubs.Select(x => new EntitySubTemporary
            {
                Col1 = x.Col1,
                Col2 = x.Col2,
                Col3 = x.Col3,
                Col4 = x.Col4,
                Col5 = x.Col5,
                Col6 = x.Col6,
                Col7 = x.Col7,
                Col8 = x.Col8,
                Col9 = x.Col9,
                Col10 = x.Col10,
                Position = i + 1
            }));
        }
        #endregion
    
        Stopwatch sw = new Stopwatch();
        sw.Start();
        using (var conn = new SqlConnection(connString))
        {
            conn.Open();
            string createTable = @"create table #EntityTemp ([No] int, Col1 varchar(50), Col2 varchar(50), Col3 varchar(50), Col4 varchar(50), Col5 varchar(50), Col6 varchar(50), Col7 varchar(50), Col8 varchar(50), Col9 varchar(50), Col10 varchar(50), Position int);";
            string createTable2 = @"create table #EntitySubTemp (Col1 varchar(50), Col2 varchar(50), Col3 varchar(50), Col4 varchar(50), Col5 varchar(50), Col6 varchar(50), Col7 varchar(50), Col8 varchar(50), Col9 varchar(50), Col10 varchar(50), Position int);";
            string insertTable = "INSERT INTO #EntityTemp ([No], Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9, Col10, Position) VALUES (@No, @Col1, @Col2, @Col3, @Col4, @Col5, @Col6, @Col7, @Col8, @Col9, @Col10, @Position)";
            string insertTable2 = "INSERT INTO #EntitySubTemp (Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9, Col10, Position) VALUES (@Col1, @Col2, @Col3, @Col4, @Col5, @Col6, @Col7, @Col8, @Col9, @Col10, @Position)";
            string saveSql = @"DECLARE @inserted0 TABLE ([Id] int, [Position] [int]);  
    MERGE into TestAddSortByDapper t
    USING #EntityTemp AS s ON 1=0  WHEN NOT MATCHED THEN  
    INSERT([No], [Col1], [Col2], [Col3], [Col4], [Col5], [Col6], [Col7], [Col8], [Col9], [Col10])
    VALUES(s.[No], s.[Col1], s.[Col2], s.[Col3], s.[Col4], s.[Col5], s.[Col6], s.[Col7], s.[Col8], s.[Col9], s.[Col10])
    OUTPUT INSERTED.[Id], s.Position INTO @inserted0;
    INSERT INTO TestAddSortByDapperSub ([Id2], [Col1], [Col2], [Col3], [Col4], [Col5], [Col6], [Col7], [Col8], [Col9], [Col10])
    SELECT i.id AS id2, s.Col1, s.Col2, s.Col3, s.Col4, s.Col5, s.Col6, s.Col7, s.Col8, s.Col9, s.Col10 
    FROM #EntitySubTemp s
    INNER JOIN @inserted0 i ON s.[Position] = i.[Position];";
            conn.Execute(createTable + "  
       " + createTable2);
            conn.Execute(insertTable, dataTemporarys);
            conn.Execute(insertTable2, dataSubTemporarys);
            conn.Execute(saveSql);
        }
        sw.Stop();
        Console.WriteLine($"通过 Dapper和临时表进行insert操作 毫时{sw.ElapsedMilliseconds}");
    }

     执行结果总结

     

    数据库执行结果也和我们sql代码一样,dapper也是用insert into table() values () 的方法一行行加代码,执行时间大概在6-7秒。

    EfCore:

     由于efcore本身就支持级联增加,所有代码比较简单

    public class TestContext : DbContext
    {
        public DbSet<Entity> Entity { get; set; }
        public DbSet<EntitySub> EntitySub { get; set; }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(connString);
        }
    }
    [Table("TestAddSortByEfCore")]
    public class Entity 
    {
        public int Id { get; set; }
        public int No { get; set; }
        public string Col1 { get; set; }
        public string Col2 { get; set; }
        public string Col3 { get; set; }
        public string Col4 { get; set; }
        public string Col5 { get; set; }
        public string Col6 { get; set; }
        public string Col7 { get; set; }
        public string Col8 { get; set; }
        public string Col9 { get; set; }
        public string Col10 { get; set; }
    
        [ForeignKey("Id2")]
        public virtual ICollection<EntitySub> EntitySubs { get; set; } = new HashSet<EntitySub>();
    }
    
    [Table("TestAddSortByEfCoreSub")]
    public class EntitySub 
    { 
        public int Id { get; set; }
        public int Id2 { get; set; }
        public string Col1 { get; set; }
        public string Col2 { get; set; }
        public string Col3 { get; set; }
        public string Col4 { get; set; }
        public string Col5 { get; set; }
        public string Col6 { get; set; }
        public string Col7 { get; set; }
        public string Col8 { get; set; }
        public string Col9 { get; set; }
        public string Col10 { get; set; }
    }
    static void AddDataByEfCoreCascade(List<Entity> datas)
    {
    int r1 = 0;
        Stopwatch sw = new Stopwatch();
        sw.Start();
        using (var db = new TestContext())
        {
            db.Entity.AddRange(datas);
            r1 = db.SaveChanges();
        }
        sw.Stop();
        Console.WriteLine($"通过 EfCore 导入数据{r1}行 毫时{sw.ElapsedMilliseconds}");
    }

    执行结果总结

     

    -- 数据库实际执行语句
    (@p0 nvarchar(4000),...,@p461 int)
    SET NOCOUNT ON;  
    DECLARE @inserted0 TABLE ([Id] int, [_Position] [int]);  
    MERGE [TestAddSortByEfCore] 
    USING (  
        VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, 0),...,(@p451, @p452, @p453, @p454, @p455, @p456, @p457, @p458, @p459, @p460, @p461, 41)
    ) AS i ([Col1], [Col10], [Col2], [Col3], [Col4], [Col5], [Col6], [Col7], [Col8], [Col9], [No], _Position) ON 1=0  
    WHEN NOT MATCHED THEN  
        INSERT ([Col1], [Col10], [Col2], [Col3], [Col4], [Col5], [Col6], [Col7], [Col8], [Col9], [No])  
        VALUES (i.[Col1], i.[Col10], i.[Col2], i.[Col3], i.[Col4], i.[Col5], i.[Col6], i.[Col7], i.[Col8], i.[Col9], i.[No])  
        OUTPUT INSERTED.[Id], i._Position  INTO @inserted0;    
    SELECT [t].[Id] FROM [TestAddSortByEfCore] t  INNER JOIN @inserted0 i ON ([t].[Id] = [i].[Id])  ORDER BY [i].[_Position]; 
    (
    @p11 nvarchar(4000),...,@p471 nvarchar(4000),@p472 int) SET NOCOUNT ON; DECLARE @inserted0 TABLE ([Id] int, [_Position] [int]); MERGE [TestAddSortByEfCoreSub] USING ( VALUES (@p11, @p12, @p13, @p14, @p15, @p16, @p17, @p18, @p19, @p20, @p21, 0),...,(@p462, @p463, @p464, @p465, @p466, @p467, @p468, @p469, @p470, @p471, @p472, 41) ) AS i ([Col1], [Col10], [Col2], [Col3], [Col4], [Col5], [Col6], [Col7], [Col8], [Col9], [Id2], _Position) ON 1=0 WHEN NOT MATCHED THEN INSERT ([Col1], [Col10], [Col2], [Col3], [Col4], [Col5], [Col6], [Col7], [Col8], [Col9], [Id2]) VALUES (i.[Col1], i.[Col10], i.[Col2], i.[Col3], i.[Col4], i.[Col5], i.[Col6], i.[Col7], i.[Col8], i.[Col9], i.[Id2]) OUTPUT INSERTED.[Id], i._Position INTO @inserted0; SELECT [t].[Id] FROM [TestAddSortByEfCoreSub] t INNER JOIN @inserted0 i ON ([t].[Id] = [i].[Id]) ORDER BY [i].[_Position];

    从结果我们可以看到,efcore使用的是Merge方式执行,总耗时14-15秒,这个在性能上是不能接受的。

    ADO.Net BulkCopy:

    static void AddDataByBulkCopyTemporary2(List<Entity> datas)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        int r = 0;
    
        using (SqlConnection cn = new SqlConnection(connString))
        {
            cn.Open();
    
            DataTable entityTable = new DataTable();
            entityTable.Columns.Add("No");
            entityTable.Columns.Add("Col1");
            entityTable.Columns.Add("Col2");
            entityTable.Columns.Add("Col3");
            entityTable.Columns.Add("Col4");
            entityTable.Columns.Add("Col5");
            entityTable.Columns.Add("Col6");
            entityTable.Columns.Add("Col7");
            entityTable.Columns.Add("Col8");
            entityTable.Columns.Add("Col9");
            entityTable.Columns.Add("Col10");
            entityTable.Columns.Add("Position");
    
            DataTable entitySubTable = new DataTable();
            entitySubTable.Columns.Add("Col1");
            entitySubTable.Columns.Add("Col2");
            entitySubTable.Columns.Add("Col3");
            entitySubTable.Columns.Add("Col4");
            entitySubTable.Columns.Add("Col5");
            entitySubTable.Columns.Add("Col6");
            entitySubTable.Columns.Add("Col7");
            entitySubTable.Columns.Add("Col8");
            entitySubTable.Columns.Add("Col9");
            entitySubTable.Columns.Add("Col10");
            entitySubTable.Columns.Add("Position");
            for (int i = 0; i < datas.Count; i++)
            {
                var item = datas[i];
                DataRow dr = entityTable.NewRow();
                dr[0] = item.No;
                dr[1] = item.Col1;
                dr[2] = item.Col2;
                dr[3] = item.Col3;
                dr[4] = item.Col4;
                dr[5] = item.Col5;
                dr[6] = item.Col6;
                dr[7] = item.Col7;
                dr[8] = item.Col8;
                dr[9] = item.Col9;
                dr[10] = item.Col10;
                dr[11] = i + 1;
                entityTable.Rows.Add(dr);
                foreach (var sub in item.EntitySubs)
                {
                    var subDr = entitySubTable.NewRow();
                    subDr[0] = sub.Col1;
                    subDr[1] = sub.Col2;
                    subDr[2] = sub.Col3;
                    subDr[3] = sub.Col4;
                    subDr[4] = sub.Col5;
                    subDr[5] = sub.Col6;
                    subDr[6] = sub.Col7;
                    subDr[7] = sub.Col8;
                    subDr[8] = sub.Col9;
                    subDr[9] = sub.Col10;
                    subDr[10] = i + 1;
                    entitySubTable.Rows.Add(subDr);
                }
            }
    
            string createTable = @"create table #EntityTemp ([No] int, Col1 varchar(50), Col2 varchar(50), Col3 varchar(50), Col4 varchar(50), Col5 varchar(50), Col6 varchar(50), Col7 varchar(50), Col8 varchar(50), Col9 varchar(50), Col10 varchar(50), Position int);";
            string createTable2 = @"create table #EntitySubTemp (Col1 varchar(50), Col2 varchar(50), Col3 varchar(50), Col4 varchar(50), Col5 varchar(50), Col6 varchar(50), Col7 varchar(50), Col8 varchar(50), Col9 varchar(50), Col10 varchar(50), Position int);";
            cn.Execute(createTable);
            cn.Execute(createTable2);
            using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(cn))
            {
                sqlBulkCopy.BatchSize = entityTable.Rows.Count;
                sqlBulkCopy.BulkCopyTimeout = 1800;
                sqlBulkCopy.DestinationTableName = "#EntityTemp";
    
                sqlBulkCopy.ColumnMappings.Add("No", "No");
                sqlBulkCopy.ColumnMappings.Add("Col1", "Col1");
                sqlBulkCopy.ColumnMappings.Add("Col2", "Col2");
                sqlBulkCopy.ColumnMappings.Add("Col3", "Col3");
                sqlBulkCopy.ColumnMappings.Add("Col4", "Col4");
                sqlBulkCopy.ColumnMappings.Add("Col5", "Col5");
                sqlBulkCopy.ColumnMappings.Add("Col6", "Col6");
                sqlBulkCopy.ColumnMappings.Add("Col7", "Col7");
                sqlBulkCopy.ColumnMappings.Add("Col8", "Col8");
                sqlBulkCopy.ColumnMappings.Add("Col9", "Col9");
                sqlBulkCopy.ColumnMappings.Add("Col10", "Col10");
                sqlBulkCopy.ColumnMappings.Add("Position", "Position");
                sqlBulkCopy.WriteToServer(entityTable);
            }
            using (SqlBulkCopy sqlBulkCopy2 = new SqlBulkCopy(cn))
            {
                sqlBulkCopy2.BatchSize = entitySubTable.Rows.Count;
                sqlBulkCopy2.BulkCopyTimeout = 1800;
                sqlBulkCopy2.DestinationTableName = "#EntitySubTemp";
    
                sqlBulkCopy2.ColumnMappings.Add("Col1", "Col1");
                sqlBulkCopy2.ColumnMappings.Add("Col2", "Col2");
                sqlBulkCopy2.ColumnMappings.Add("Col3", "Col3");
                sqlBulkCopy2.ColumnMappings.Add("Col4", "Col4");
                sqlBulkCopy2.ColumnMappings.Add("Col5", "Col5");
                sqlBulkCopy2.ColumnMappings.Add("Col6", "Col6");
                sqlBulkCopy2.ColumnMappings.Add("Col7", "Col7");
                sqlBulkCopy2.ColumnMappings.Add("Col8", "Col8");
                sqlBulkCopy2.ColumnMappings.Add("Col9", "Col9");
                sqlBulkCopy2.ColumnMappings.Add("Col10", "Col10");
                sqlBulkCopy2.ColumnMappings.Add("Position", "Position");
                sqlBulkCopy2.WriteToServer(entitySubTable);
            }
    
            string sql = @"DECLARE @inserted0 TABLE ([Id] int, [Position] [int]);  
    MERGE into TestAddSortByBulkCopy t
    USING #EntityTemp AS s ON 1=0  WHEN NOT MATCHED THEN  
    INSERT ([Col1], [Col2], [Col3], [Col4], [Col5], [Col6], [Col7], [Col8], [Col9], [Col10], [No])  
    VALUES (s.[Col1], s.[Col2], s.[Col3], s.[Col4], s.[Col5], s.[Col6], s.[Col7], s.[Col8], s.[Col9], s.[Col10], s.[No])  
    OUTPUT INSERTED.[Id], s.Position  INTO @inserted0;
    insert into TestAddSortByBulkCopySub ([Id2], [Col1], [Col2], [Col3], [Col4], [Col5], [Col6], [Col7], [Col8], [Col9], [Col10])
    select i.id AS id2, s.Col1, s.Col2, s.Col3, s.Col4, s.Col5, s.Col6, s.Col7, s.Col8, s.Col9, s.Col10 
    from #EntitySubTemp s
        inner join @inserted0 i on s.[Position] = i.[Position] ";
            r = cn.Execute(sql);
        }
        sw.Stop();
        Console.WriteLine($"通过 BulkCopy 导入数据{r}行  毫时{sw.ElapsedMilliseconds}");
    }

    执行结果总结

     两篇文章进行总结,dapper采用insert into table() values () 方式一行行加数据,但性能上还是挺不错的;efcore当数据大于两行则采用Merge方式,性能上略低于dapper,级联上性能比较差了,最严重的问题是批量插入顺序不对;freesql采用insert into table() values (), (), ()一次性增加多行,单表查询性能是最差的,估计是代码上问题而不是sql语句问题;Bulkcopy的性能是最好的,毕竟他是ADO.net针对大量数据而设计的。

  • 相关阅读:
    Redis——各个数据类型最大存储量
    Redis——数据结构
    MongoDB——分片片键的选择 (转)
    Java—— ThreadLocal (转)
    Python——关于安装和基本命令记录
    Unity3d——UI框架的设计 (转)
    Java——中介者(Mediator)模式 (转)
    Java——Java基础——100个问题汇总 (转)
    Actor——Actor模型介绍 (转)
    run keyword if
  • 原文地址:https://www.cnblogs.com/Cxiaoao/p/14655535.html
Copyright © 2011-2022 走看看