马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
以下是一个基于 Entity Framework Extensions 的 BulkDelete 高效删除海量数据的方案,包含性能优化、稳定性保障和错误处理机制:
csharp
using System;
using System.Linq;
using Z.EntityFramework.Plus;
public class BulkDeleteService
{
private const int BatchSize = 50000; // 根据服务器内存和网络调解
private const int MaxRetryAttempts = 3;
private readonly TimeSpan RetryDelay = TimeSpan.FromSeconds(5);- public void BulkDeleteRecords(MyDbContext context, DateTime deleteBefore)
- {
- var query = context.YourEntities
- .Where(x => x.CreatedDate < deleteBefore)
- .OrderBy(x => x.Id); // 按主键排序确保分页稳定
- int totalDeleted = 0;
- bool hasMoreData = true;
-
- var deleteOptions = new BulkDeleteOptions
- {
- BatchSize = BatchSize,
- UseTransaction = false, // 关闭事务提升性能
- CommandTimeout = 600, // 适当延长超时时间
- ExecuteHook = (x) =>
- {
- // 内存优化配置
- x.BatchTimeout = 60;
- x.TrackingEntities = false;
- }
- };
- while (hasMoreData)
- {
- try
- {
- // 分页删除(使用子查询确保分页准确)
- var subQuery = query.Take(BatchSize);
- int deletedCount = RetryPolicy(() => subQuery.DeleteFromQuery(deleteOptions));
- totalDeleted += deletedCount;
- hasMoreData = deletedCount == BatchSize;
- Console.WriteLine($"Deleted {totalDeleted:N0} records, LastID: {GetLastProcessedId(context)}");
-
- // 定期释放资源
- if (totalDeleted % (BatchSize * 10) == 0)
- {
- context.Dispose();
- context = new MyDbContext();
- query = context.YourEntities.Where(...); // 重建查询
- }
- }
- catch (Exception ex)
- {
- LogError(ex, totalDeleted);
- throw; // 根据业务需求决定是否终止
- }
- }
- }
- private int RetryPolicy(Func<int> action)
- {
- int attempts = 0;
- while (true)
- {
- try
- {
- return action();
- }
- catch (SqlException ex) when (attempts < MaxRetryAttempts)
- {
- if (IsTransientError(ex.Number))
- {
- attempts++;
- Thread.Sleep(RetryDelay);
- continue;
- }
- throw;
- }
- }
- }
- private bool IsTransientError(int errorNumber)
- {
- // SQL Server 瞬态错误代码列表
- int transientErrors = { 4060, 40197, 40501, 40613, 49918, 49919, 49920, 4221 };
- return transientErrors.Contains(errorNumber);
- }
- private long GetLastProcessedId(MyDbContext context)
- {
- return context.YourEntities
- .OrderByDescending(x => x.Id)
- .Select(x => x.Id)
- .FirstOrDefault();
- }
- private void LogError(Exception ex, int processedCount)
- {
- // 记录错误和当前进度到持久化存储
- File.AppendAllText("delete_log.txt",
- $"{DateTime.UtcNow:u} Error after {processedCount} records: {ex}\n");
- }
复制代码 }
关键优化点:
- 使用 Take(BatchSize).DeleteFromQuery() 确保准确分页
- 按主键排序制止数据重复或遗漏
- 通过子查询实现稳定分页
- 禁用实体跟踪 (TrackingEntities = false)
- 批量提交而非事务包裹整个操作
- 定期重建 DbContext 释放内存
- 调解批量大小(建议5万-10万/批)
- 瞬态错误重试机制(支持SQL超时、毗连中断等)
- 进度记录到文件(支持断点续删)
- 独立的错误日记记录
- 内存走漏预防(定期释放上下文)
- 数据库优化建议:
sql
-- 实行前建议操作
ALTER DATABASE YourDB SET RECOVERY SIMPLE;
EXEC sp_configure 'max server memory', '4096'; -- 根据服务器调解
ALTER DATABASE YourDB SET DELAYED_DURABILITY = FORCED;
-- 实行后建议
UPDATE STATISTICS YourTable WITH FULLSCAN;
ALTER INDEX ALL ON YourTable REBUILD;
- 建议在低峰时段实行
- 提前备份目的数据
- 使用数据库镜像/AlwaysOn在从库实行
- 监控锁状态(sp_who2, sys.dm_tran_locks)
<ol start="6">高级方案:
csharp
// 分区并行删除(需要物理分区)
Parallel.ForEach(GetPartitionRanges(), range =>
{
using var ctx = new MyDbContext();
ctx.YourEntities
.Where(x => x.Id > range.Start && x.Id x.CreatedDate >= range.Start && x.CreatedDate < range.End)
.DeleteFromQuery();
}</p>注意事项:
- 建议先在测试情况验证删除逻辑
- 确保删除条件字段有符合的索引(CreatedDate或ID)
- 监控事务日记增长(特别是FULL恢复模式)
- 思量使用Table Partitioning处理持续的大数据删除
对于超大规模数据(超过1亿条),建议结合数据库原生工具(如SQL Server的Partition Switching)实现秒级数据删除。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|