C#对50000条及更多记录执行批量删除

时间:2018-10-03 12:11:43

标签: c# asp.net asp.net-mvc c#-4.0 bulk

我遇到一个问题,其中有一段如下代码:

  var inPast = DateTime.Today.AddDays(-30);
    DBRetry.Do(() => EFBatchOperation.For(ctx, ctx.Transactions).Where(t =>  t.TransactionDate <= inPast).Delete(), TimeSpan.FromSeconds(2));

如果发生超时或死锁,DBRetry函数仅每2秒重复一次操作...

现在,这里的问题是我的Transactions表包含1亿多条记录...

如您所见,我正在尝试删除所有早于30天的记录...但是,由于我不断出现如下所示的超时情况,因此该操作无效:

  

执行超时已过期。逾时时间已过   操作完成或服务器没有响应。

对列TransactionDate进行了索引,它是一个非唯一且非聚集的索引,但这似乎无济于事...我用来执行批量删除的库就是这个:

https://github.com/MikaelEliasson/EntityFramework.Utilities

有人知道更有效的解决方案,或者我怎么解决这个问题?

5 个答案:

答案 0 :(得分:1)

您可以尝试增加Db上下文的命令超时:

(原为MSDN

public class YourContext : DbContext
{
  public YourContext()
    : base("YourConnectionString")
 {
    // Get the ObjectContext related to this DbContext
    var objectContext = (this as IObjectContextAdapter).ObjectContext;

    // Sets the command timeout for all the commands
    objectContext.CommandTimeout = 120;
  }
}

答案 1 :(得分:1)

我会做些事情来减轻这个问题;

1)实体框架往往要求在允许您删除记录/对象之前先将它们加载到内存中,这本身可能会对性能造成重大影响。

最好为此操作运行一些自定义SQL

2)在数据库表中为字段交易日期创建索引

想象一下,在大型表中运行一个select-> where查询必须执行的操作,给定足够大的记录集,它必须扫描所有这些记录以确定您需要哪些记录。向该表添加索引有助于为数据库提供您最查询的字段线索,并使数据库为您优化这些操作。

3)运行查询超过30天

假设这是一项常规的内部整理操作,那么运行30天以上将使数据库表中的行数保持最少。在某些数据库中,您甚至可以添加时间表,因此无需在代码中包括它。

4)批量删除记录

如果必须为此使用Entity Framework,则可以选择要删除的行(数量为X);这可以帮助分散数据库上的负载,以防万一该操作可能需要花费几分钟的时间来执行。

答案 2 :(得分:1)

我认为索引不是解决方案,而可能是问题所在。 如果表在几列上都有索引,则删除操作可能会锁定记录以更新索引。这是昂贵的,并且花费时间尝试将其分解。获取记录的ID列表,然后将其删除,例如说10,000

答案 3 :(得分:0)

由于删除5万行可能要花费2秒钟以上的时间,因此请以较小的块为单位进行删除;说1000。

此外,请勿“每2秒执行一次”;而是“持续进行”。也就是说,在完成一批之后,再进行下一批。 (可选)在批次之间短暂暂停。按固定的时间表执行删除操作可能会导致绊倒多个同时运行的副本。

这里讨论了几种技术:http://mysql.rjweb.org/doc.php/deletebig

请注意,该链接建议PARTITION BY RANGE(TO_DAYS())是进行大型删除的最佳方法。建议每天(或每周)进行分区。

答案 4 :(得分:0)

DbContext.Database.CommandTimeout = 0; // set unlimited timeout

或在配置文件中将其设置为0

https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.commandtimeout?view=netframework-4.7.2