【问题标题】:Running an inserting to database task more faster更快地运行插入数据库任务
【发布时间】:2016-05-23 11:09:31
【问题描述】:

我问了一个question,那是我怎样才能更快地运行任务,但没有一个答案对我有用。我有以下代码将 1500 条记录插入数据库,但问题是大约需要 4 秒。我使用了async/await、并行循环和AddRange,并且我在保存时禁用了更改自动检测和验证,但它们都没有效果。我的代码是这样的:

async void button7_Click(object sender, EventArgs e)
{
    var task = await Task.Run(() =>
    {
        Random rnd = new Random();
        for (int i = 0; i <= 1500; i++)
        {
            db.Tbls.Add(new Tbl()
            {
                Name = "User" + i + 1,
                Num = rnd.Next(10, i + 10) / 10
            });
        }

        db.SaveChanges();
        return db.Tbls.Count();

    });
}

加上 AddRange:

async void button7_Click(object sender, EventArgs e)
{
    var task = await Task.Run(() =>
    {
        Random rnd = new Random();
        var tbls = new List<Tbl>();
        for (int i = 0; i <= 1500; i++)
        {
            tbls.Add(new Tbl()
            {
                Name = "User" + i + 1,
                Num = rnd.Next(10, i + 10) / 10
            });
            progress.Report(i * 100 / 1500);
         }
        db.Tbls.AddRange(tbls);
        db.SaveChanges();
        return db.Tbls.Count();

    });
}

并带有并行循环:

var task = await Task.Run(() =>
{
    int seed = Environment.TickCount;
    var random = new ThreadLocal<Random>(() => new Random(Interlocked.Increment(ref seed)));
    var tbls = new ConcurrentBag<Tbl>();
    Parallel.For(0, 1500, (i) => {
        tbls.Add(new Tbl()
        {
            Name = "User" + i + 1,
            Num = random.Value.Next(10, i + 10) / 10
        });
    });                
    db.Tbls.AddRange(tbls);
    db.SaveChanges();
    return db.Tbls.Count();
});

有谁知道问题出在哪里?

【问题讨论】:

  • 你的表有主键吗?在没有任何异步和/或并行执行的情况下插入 1500 条记录需要多长时间?查看 SQL Profiler 输出,除了您看到的 insert 命令之外,还有哪些“不可见”操作?
  • 是的。我的表有主键。无论有没有异步和/或并行执行,都需要大约 4 秒。我也使用了代码优先的方法。
  • 在任何这些 sn-ps 中都没有并行数据库访问。所有这些 sn-ps 都做同样的事情 - 同步调用 SaveChanges

标签: c# sql-server ef-code-first async-await task-parallel-library


【解决方案1】:

如果您想一次性将大量行加载到数据库中,那么您可能会从批量加载中获得更好的性能。如果您加载单个行,我可以想象 1500 次插入很容易花费 4 秒 - 而且您可能不会让单个插入运行得更快,因为主要瓶颈是任何数据库事务所需的最小 I/O。

使用批量加载 API - 例如SQLBulkCopy - 您准备一个记录集,然后一键将其加载到数据库中。这将比 1,500 个单独的插入要快得多。

您尚未指定需要具有支持批量加载的驱动程序的数据库平台。 SQL Server 可以;除非您购买某些版本,否则某些(例如)Oracle 不会。

【讨论】:

  • 我使用 sql server。我确信我的代码至少应该运行不到一秒。但我不知道为什么以及是什么问题导致它花费了太长时间。
  • 你能给我看一个实体框架的例子吗?
  • OTOH 我不知道实体框架是否支持批量加载。您可能需要直接使用 SQLBulkCopy API。
  • 好吧,我的老问题的一位回答者说:“我已经对此进行了测试,并且使用 AddRange 而不是 Add 在循环中,我需要 700 毫秒,而原始代码需要 6.6 秒。”所以我确定这应该可以通过实体框架实现,但我不知道怎么做?
  • 在您之前的 here 中讨论的 codeplex 上有一个用于 EF 的批量插入扩展。它可能会做你想做的事。然而,如果你不能从中得到你需要的东西,你可能不得不直接使用批量插入。
猜你喜欢
  • 1970-01-01
  • 2018-10-23
  • 2012-07-27
  • 2015-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多