C# 在每次迭代中处理内存中的对象
我正在编写一个对现有webjob的增强,它从一个表(外圈)获取记录并将其推送到另一个表(内圈)。外圈表中大约有7500万条记录,我想对要推送到内圈表的数据进行非常有选择性的选择。我使用一个存储过程来获取数据,对其进行分页,然后将其返回到内环中C# 在每次迭代中处理内存中的对象,c#,out-of-memory,C#,Out Of Memory,我正在编写一个对现有webjob的增强,它从一个表(外圈)获取记录并将其推送到另一个表(内圈)。外圈表中大约有7500万条记录,我想对要推送到内圈表的数据进行非常有选择性的选择。我使用一个存储过程来获取数据,对其进行分页,然后将其返回到内环中 public async Task ExecuteAsync( AMS360DbContext outerRingDb , MiddleTierCoreDbContext innerRingDb ,
public async Task ExecuteAsync(
AMS360DbContext outerRingDb
, MiddleTierCoreDbContext innerRingDb
, SyncSupportDbContext syncDb
, ILogger logger
, CancellationToken cancellationToken)
{
var log = logger.CoverageSync(this);
TelemetryClient telemetry = new TelemetryClient();
log.SyncStarted("Coverages");
var sw = Stopwatch.StartNew();
var transformer = new AmsPolicyCoveragesTransformer();
const string innerRingTable = InnerRingTables.PolicyCoverages;
const string sourceSchema = "ams360";
const string sourceStoredProcedure = "GetPolicyCoverages";
try
{
const int pageSize = 50_000;
var page = 0;
bool hasMoreRecords;
var rowVersion = await syncDb.GetInnerRingRowVersionAsync(innerRingTable, sourceSchema, sourceStoredProcedure);
do
{
if (cancellationToken.IsCancellationRequested)
return;
var index = page * pageSize;
log.SyncInProgress(index, index + pageSize - 1);
var rowVersionParam = new SqlParameter()
{
ParameterName = "@RowVersion",
SqlDbType = SqlDbType.Timestamp,
Direction = ParameterDirection.Input,
Value = rowVersion != null ? BitConverter.GetBytes(Convert.ToUInt64(rowVersion)).Reverse().ToArray() : (object)DBNull.Value
};
var prms = new SqlParameter[]
{
new SqlParameter("@PageStart", index),
new SqlParameter("@PageSize", pageSize),
rowVersionParam
};
var outerRingCoverages = await outerRingDb.Set<SpPolicyCoverages>()
.FromSqlRaw("EXEC ams360.GetPolicyCoverages @PageStart, @PageSize, @RowVersion", prms)
.ToListAsync(cancellationToken);
page++;
var transformed = transformer.Transform(outerRingCoverages).ToList();
if (transformed.Any())
{
await MergeToInnerRingAsync(innerRingDb, transformed, cancellationToken);
var latestVersion = outerRingCoverages.Max(x => x.RowVersion);
telemetry.TrackEvent("Inner Ring Sync - Coverages Success");
await syncDb.UpdateInnerRingSyncRowVersionAsync(innerRingTable, sourceSchema, sourceStoredProcedure, latestVersion.Value, cancellationToken);
}
hasMoreRecords = (outerRingCoverages.Count == pageSize);
} while (hasMoreRecords);
}
catch (Exception ex)
{
log.SyncError("Coverages", ex);
telemetry.TrackEvent("Inner Ring Sync - Coverages Error");
throw;
}
log.SyncFinished("Coverages", sw.Elapsed);
}
公共异步任务ExecuteAsync(
AMS360DbContext outerRingDb
,MiddleTierCoreDbContext innerRingDb
,SyncSupportDbContext syncDb
,ILogger记录器
,CancellationToken CancellationToken)
{
var log=logger.CoverageSync(this);
遥测客户端遥测=新的遥测客户端();
log.SyncStarted(“覆盖范围”);
var sw=Stopwatch.StartNew();
var变压器=新的AMSPOLICYCOVERAGESTRANSOR();
const string innerRingTable=InnerRingTables.policyCoverage;
常量字符串sourceSchema=“ams360”;
常量字符串sourceStoredProcedure=“GetPolicyCoverage”;
尝试
{
const int pageSize=50_000;
var-page=0;
布尔·哈斯莫尔唱片公司;
var rowVersion=await syncDb.GetInnerRingRowVersionAsync(innerRingTable、sourceSchema、SourceStoredProcess);
做
{
if(cancellationToken.IsCancellationRequested)
回来
变量索引=页面*页面大小;
同步进程(索引,索引+页面大小-1);
var rowVersionParam=新的SqlParameter()
{
ParameterName=“@RowVersion”,
SqlDbType=SqlDbType.Timestamp,
方向=参数方向。输入,
Value=rowVersion!=null?位转换器.GetBytes(Convert.ToUInt64(rowVersion)).Reverse().ToArray():(对象)DBNull.Value
};
var prms=新的SqlParameter[]
{
新的SqlParameter(“@PageStart”,索引),
新的SqlParameter(“@PageSize”,PageSize),
rowVersionParam
};
var outerringcoverage=await outerRingDb.Set()
.FromSqlRaw(“EXEC ams360.getPolicyCoverage@PageStart、@PageSize、@RowVersion”、prms)
.ToListSync(取消令牌);
page++;
var transformed=transformer.Transform(outerringcoverage.ToList();
if(transformed.Any())
{
等待合并连接同步(innerRingDb、转换、取消令牌);
var latestVersion=outerringcoverage.Max(x=>x.RowVersion);
TrackEvent(“内环同步-覆盖成功”);
等待syncDb.UpdateInnerRingSyncRowVersionAsync(innerRingTable、sourceSchema、SourceStoredProcess、latestVersion.Value、cancellationToken);
}
hasMoreRecords=(outerringcoverage.Count==页面大小);
}while(hasMoreRecords);
}
捕获(例外情况除外)
{
log.SyncError(“覆盖范围”,例如);
遥测.TrackEvent(“内环同步-覆盖错误”);
投
}
日志同步已完成(“覆盖范围”,软件运行时间);
}
我在这段代码中遇到的问题是,在迭代过程中的某个时刻,我遇到了OutofMemory异常。因此,我在查看诊断工具时,惊讶地发现,当我从SPPolicyCoverage表中获得50000条记录时,在内存中的堆中以及在下一次迭代中创建了50000个对象,内存中创建了100000个对象,并且随着这些对象继续在内存中累积,直到遇到OutofMemory异常。在每次迭代中处理对象(SPPolicyCoverage)的最佳方法是什么,这样我就不会遇到OutofMemory异常?请提前通知..谢谢。SPPolicyCoverage是实体类型吗?如果是这样的话,这可能是一个问题,因为数据库上下文上没有禁用ChangeTracking。更改跟踪并没有什么不同,只是因为您使用的是FromSqlRaw。尝试禁用上下文上的更改跟踪,或使用
.AsNoTracking()
:
var outerringcoverage=await outerRingDb.Set()
.FromSqlRaw(“EXEC ams360.getPolicyCoverage@PageStart、@PageSize、@RowVersion”、prms)
.AsNoTracking()//不要将“处理”与“释放内存”混淆。他们不是一回事。“Disposing”在C#中的意思非常具体。这些上下文对象是什么?实体框架?根据您的描述,我认为这项工作应该在sql server中一起完成。为什么提取这么多数据只是为了将其推回到服务器,让服务器处理作业不是更有效吗。要使0获取/推送数据?但也许我误解了你的问题。我会尝试在每次迭代中创建“上下文”对象项的新实例。如果这些是实体框架上下文,objectstatemanager将在加载后保持它们的状态不变,或者至少手动将它们与上下文分离。您完全正确。导致问题的原因是没有添加.AsNoTracking()。非常感谢您的帮助:)
var outerRingCoverages = await outerRingDb.Set<SpPolicyCoverages>()
.FromSqlRaw("EXEC ams360.GetPolicyCoverages @PageStart, @PageSize, @RowVersion", prms)
.AsNoTracking() // <--
.ToListAsync(cancellationToken);