C# 对 50000 条及更多记录执行批量删除
C# perform bulk delete on 50000 and more records
我有一个问题,我有一段代码如下:
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
table 包含超过 1 亿条记录...
如您所见,我正在尝试删除所有超过 30 天的记录...但是这不起作用,因为我经常遇到超时,如下所示:
Execution Timeout Expired. The timeout period elapsed prior to
completion of the operation or the server is not responding.
列 TransactionDate
已编入索引,它是一个非唯一且非聚集索引,但这似乎没有帮助...我用来执行批量删除的库是这个:
https://github.com/MikaelEliasson/EntityFramework.Utilities
有谁知道更有效的解决方案或我如何解决这个问题?
您可以尝试增加 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) Entity Framework 往往要求先将 records/objects 加载到内存中,然后再允许您删除它们,这本身就是一个很大的性能损失。
对于此操作,运行 一些自定义 SQL 可能会更好
2) 在数据库中为字段交易日期table创建索引
想象一下数据库在 table 到 运行 一个 select->where 查询中必须做什么,给定一个足够大的记录集,它必须扫描所有他们来确定您需要哪些记录。向此添加索引 table 有助于为数据库提供您查询最多的字段的线索,并让数据库为您优化这些操作。
3) 运行 查询频率超过 30 天
假设这是一个常规的内务管理操作,运行将其设置的频率超过 30 天将使数据库中的行数 table 保持在最低限度。在某些数据库中,您甚至可以添加计划,因此无需在代码中包含它。
4) 批量删除记录
如果你必须为此使用Entity Framework,你可以select你想删除的行,批量为X;这有助于分配数据库上的负载,以防操作可能需要很长时间才能执行。
我认为索引不是解决方案,可能是问题所在。
如果 table 在多个列上有索引,则删除可能会锁定记录以更新索引。这是昂贵的,并且需要时间尝试将其分解。获取记录的 ID 列表,然后分批删除它们,比如 10,000
由于删除 50K 行可能需要超过 2 秒的时间,因此请分小块进行;说 1000。
还有,不要"do it every 2 seconds";相反 "do it continually"。也就是说,完成一批后,再做下一批。可选择在批次之间短暂暂停。按固定时间表执行删除操作可能会导致同时遇到多个副本 运行。
这里讨论了几种技术:http://mysql.rjweb.org/doc.php/deletebig
请注意,link 建议 PARTITION BY RANGE(TO_DAYS())
作为执行大删除的最佳方式。建议每日(或每周)分区。
DbContext.Database.CommandTimeout = 0; // set unlimited timeout
或者在配置文件中设置为0
我有一个问题,我有一段代码如下:
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
table 包含超过 1 亿条记录...
如您所见,我正在尝试删除所有超过 30 天的记录...但是这不起作用,因为我经常遇到超时,如下所示:
Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
列 TransactionDate
已编入索引,它是一个非唯一且非聚集索引,但这似乎没有帮助...我用来执行批量删除的库是这个:
https://github.com/MikaelEliasson/EntityFramework.Utilities
有谁知道更有效的解决方案或我如何解决这个问题?
您可以尝试增加 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) Entity Framework 往往要求先将 records/objects 加载到内存中,然后再允许您删除它们,这本身就是一个很大的性能损失。
对于此操作,运行 一些自定义 SQL 可能会更好
2) 在数据库中为字段交易日期table创建索引
想象一下数据库在 table 到 运行 一个 select->where 查询中必须做什么,给定一个足够大的记录集,它必须扫描所有他们来确定您需要哪些记录。向此添加索引 table 有助于为数据库提供您查询最多的字段的线索,并让数据库为您优化这些操作。
3) 运行 查询频率超过 30 天
假设这是一个常规的内务管理操作,运行将其设置的频率超过 30 天将使数据库中的行数 table 保持在最低限度。在某些数据库中,您甚至可以添加计划,因此无需在代码中包含它。
4) 批量删除记录
如果你必须为此使用Entity Framework,你可以select你想删除的行,批量为X;这有助于分配数据库上的负载,以防操作可能需要很长时间才能执行。
我认为索引不是解决方案,可能是问题所在。 如果 table 在多个列上有索引,则删除可能会锁定记录以更新索引。这是昂贵的,并且需要时间尝试将其分解。获取记录的 ID 列表,然后分批删除它们,比如 10,000
由于删除 50K 行可能需要超过 2 秒的时间,因此请分小块进行;说 1000。
还有,不要"do it every 2 seconds";相反 "do it continually"。也就是说,完成一批后,再做下一批。可选择在批次之间短暂暂停。按固定时间表执行删除操作可能会导致同时遇到多个副本 运行。
这里讨论了几种技术:http://mysql.rjweb.org/doc.php/deletebig
请注意,link 建议 PARTITION BY RANGE(TO_DAYS())
作为执行大删除的最佳方式。建议每日(或每周)分区。
DbContext.Database.CommandTimeout = 0; // set unlimited timeout
或者在配置文件中设置为0