【导读】EF Core 5.0伴随着.NET 5.0发布已有一段时日,本节我们来预估当大批量新增数据时,大概是多少区间我们应该考虑SQLBulkCopy而不是EF Core
SQLBulkCopy早出现于.NET Framework 2.0,将数据批量写入利用此类毫无疑问最佳,虽其来源任意,但此类仅适用于SQL Server,每个关系数据库都有其批量处理驱动,这里我们仅仅只讨论SQL Server
性能差异预估批量数据大小
首先给出我们需要用到的测试模型
public class User
{public int Id { get; set; }public string Name { get; set; }public DateTime Birth { get; set; }public string Email { get; set; }public string Phone { get; set; }
}
接下来我们则需要模拟数据,为伪造实际生产数据,这里我们介绍一个包Bogus,此包专用来伪造数据,一直在更新从未间断,版本也达到32,如下:
此包中有针对用户类的模拟,具体使用这里就不详细展开,我们构造一个方法来伪造指定数量的用户数据,如下:
private IEnumerable<User> GenerateUseres(int count)
{var profileGenerator = new Faker<User>().RuleFor(p => p.Name, v => v.Person.UserName).RuleFor(p => p.Birth, v => v.Person.DateOfBirth).RuleFor(p => p.Email, v => v.Person.Email).RuleFor(p => p.Phone, v => v.Person.Phone);return profileGenerator.Generate(count);
}
有了批量伪造数据,接下来我们再利用上下文去新增数据,然后分别打印伪造数据和新增成功所耗费时间,如下:
[HttpPost]
public async Task<IActionResult> GenerateInsert([FromQuery] int count = 1000)
{var s = new Stopwatch();s.Start();var users = GenerateUseres(count);var gererationTime = s.Elapsed.ToString();s.Restart();await _context.Users.AddRangeAsync(users);var insertedCount = await _context.SaveChangesAsync();return Ok(new{inserted = insertedCount,generationTime = gererationTime,insertTime = s.Elapsed.ToString()});
}
新增100条数据太小,这里我们直接从批量1000条数据开始测试,此时我们将看到所存储数据和实际数据完全一毛一样
通过SQL Server Profiler工具监控得到如下一堆语句
通过运行多次,当然也和笔记本配置有关(i7,6核12线程,内存16G),但还是可以预估批量新增1000条大概耗时为毫秒级,如下:
接下来我们试试新增1万条看看,耗时基本需要1秒,如下:
最后我们再来试试新增十万条数据试试,大概需要14秒才能完成
到了这里想必我们没有必要再往上增长数据,我们来看看利用SqlBulkCopy又将如何
[HttpPost]
public async Task<IActionResult> GenerateAndInsertWithSqlBulkCopy([FromQuery] int count = 1000)
{var s = new Stopwatch();s.Start();var users = GenerateUseres(count);var gererationTime = s.Elapsed.ToString();s.Restart();var dt = new DataTable();dt.Columns.Add("Id");dt.Columns.Add("Name");dt.Columns.Add("Birth");dt.Columns.Add("Phone");dt.Columns.Add("Email");foreach (var user in users){dt.Rows.Add(string.Empty, user.Name, user.Birth, user.Phone, user.Email);}using var sqlBulk = new SqlBulkCopy(@"Server=.;Database=EFCore;Trusted_Connection=True;"){DestinationTableName = "Users"};await sqlBulk.WriteToServerAsync(dt);return Ok(new{inserted = dt.Rows.Count,generationTime = gererationTime,insertTime = s.Elapsed.ToString()});
}
因如上利用EF Core新增时间在毫秒级,那么我们则直接从新增1万条开始测试,如下我们可看到此时与EF Core新增1万条数据差异,耗时远远小于1秒
最后我们再来测试10万条,很显然EF Core耗时结果将为SqlBulkCopy的指数倍(大致14倍,若数据为100万,想想二者其性能差异),如下:
若继续通过SQL Server Profiler监控工具查看SQL语句,很显然SQL语句会很简短,据我所知,SqlBulkCopy是直接将数据通过流形式传输到数据库服务器,然后一次性插入到目标表中,所以性能是杠杠的。
利用SqlBulkCopy和EF Core 5.0,当然理论上不论是EF Core更新到其他任何版本,其性能与SqlBulkCopy不可同日而语,本文我们只是稍加探讨下数据量达到多少时不得不考虑其他手段来处理,而不是利用EF Core新增数据
???? EF Core和SqlBulkCopy性能差异比较,毫无疑问SqlBulkCopy为最佳选手
???? 当新增数据量达到1万+时,若需考虑性能,可采用SqlBulkCopy或其他手段处理数据而不再是EF Core,二者性能差异将呈指数增长