【问题标题】:Extreme Slowness with Linq on mass update大规模更新时 Linq 极度缓慢
【发布时间】:2013-10-24 20:02:40
【问题描述】:

我想更新可能有一百万条记录。我喜欢 Linq,我在下面写了这个来尝试完成这个。我真的希望我写这个可怕的错误。跑了好几个小时。我用 T-sql 编写它,它在几分钟内运行。有没有更好的方法可以用 linq 做到这一点?

 private static void UpdateZipCodes()
            {
                Console.WriteLine("Update Zip Codes Started");
                DataDataContext _db = new DataDataContext();


                var newBadZips = (from n in _db.NewCars
                                  where n.loczip == null || n.loczip == ""
                                  select n).ToList();
                foreach (var bad in newBadZips)
                {
                    string lZip = _db.Dealers.Where(x => x.DealerID == bad.DealerID).Select(x => x.Zipcode).SingleOrDefault();

                    bad.loczip = lZip;
                    _db.SubmitChanges();


                }
                Console.WriteLine("Update Zip Codes Finshed");
            }

T-Sql 版本

UPDATE n

SET n.loczip = d.Zipcode

FROM            NewCars AS n INNER JOIN
                         Dealers AS d ON n.DealerID = d.DealerID
                         where n.loczip is null or n.loczip = ''

我明白 linq 的明显作用是做得更多。我想知道如何改进我的 linq 查询?

我并不想比较它们,因为我知道 T-sql 是实现这一目标的正确方法。这主要是为了让我更擅长使用 linq。我确信我会想在某一时刻以较小的规模做这样的事情,并希望在 linq 中正确地做到这一点。

【问题讨论】:

  • “使用删除”是什么意思?
  • 我可能是错的,但可能是因为您在代码中与 T-SQL 版本相比执行了 3 种不同的操作。您正在查询数据库并保存一个列表,然后您正在遍历该列表,然后您正在从该列表更新数据库。拥有数百万条记录,这将需要相当长的时间。
  • 嗯,ef 不适合批量更新、插入、删除,无论你想要什么。对于批处理操作,请使用原始 sql、批量复制,而不是 EF 和 linq。
  • 在查询本身中进行连接,在迭代时更新记录,提交更改一次
  • @Servy 你说得对,我错过了涉及尺寸的部分。新计划 - 提交更改的频率远低于每条记录

标签: c# sql linq tsql


【解决方案1】:

您正在循环中查询数据库中的每个项目。那很糟。它几乎总是错误的,而且几乎总是很慢。

就像您在 T-SQL 代码中所做的那样,在您的 LINQ 代码中使用 Join 以在一个查询中获取您需要的所有信息。

接下来,不要在每条记录后更新数据库;而是批量更新它们以减少网络流量。您将无法一次完成一百万,但您可以每 100 或 1000 行或任何行提交一个批次(尝试使用不同的批次大小值,看看哪种效果最好)。

另外,不要在拥有一百万条记录的表上调用 ToList。只需迭代查询,以便查询提供程序可以流式传输数据。

var newBadZips = (from car in _db.NewCars
                        where car.loczip == null || car.loczip == ""
                        join dealer in  _db.Dealers
                        on car.DealerID equals dealer.DealerID
                        into dealers
                        select new{
                            car,
                            zip = dealers.Select(d => d.Zipcode)
                            .SingleOrDefault(),
                        });

int count = 0;
int batchSize = 1000;
foreach(var row in newBadZips)
{
    row.car.loczip = row.zip;
    if(++count % batchSize == 0)
        _db.SubmitChanges();
}

【讨论】:

  • 请注意,即使在这种情况下,LINQ 代码仍然比直接发出更新语句要慢得多。
  • @ChrisLively 可能,是的。但几乎没有那么多,而且很可能处于可以接受的水平。
  • 我并不想比较它们,因为我知道 T-sql 是实现这一目标的正确方法。这主要是为了让我更擅长使用 linq。我确信我会想在某一时刻以较小的规模做这样的事情,并希望在 linq 中正确地做到这一点。
  • “T-sql 是完成此任务的正确方法”,无法从您在这里给我们的内容中辨别出来——您使用 ORM 是有原因的,除非您有非常令人信服的时间限制,和/或替代方案是比您的 ORM 中的 正确 实现慢几个数量级,使用一致的数据模型应该赢得您没有获得的性能提升正在测量。
  • 是这样吗? on car.DealerID equals car.DealerID 我想on car.DealerID equals Dealer.DealerID
【解决方案2】:

在这种情况下提高 LINQ 速度的绝对最佳方法就是不使用 LINQ。要么直接运行 T-SQL 更新查询,要么将其放入 proc 并调用它。

现在,既然您显然喜欢 LINQ,我为什么要提倡这一点?很简单:LINQ 没有为用例提供任何好处,同时也损害了性能。简而言之,它是这项工作的错误工具。要更好地使用某物,其中一部分就是要知道何时不适合使用它。

【讨论】:

  • 同意; LINQ 可能读起来不错,但它通过向开发人员隐藏代码实际执行的操作而产生了很多问题。我见过人们在 LINQ 查询中使用反射......呃!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-16
  • 1970-01-01
  • 1970-01-01
  • 2020-12-03
相关资源
最近更新 更多