我在sql server中有一个表 dbo.cache ,带有两个列:
- 键:varchar(50)
- 值:nvarchar(max)
我打算将大字符串存储在价值列(> 30MB)中,并在多个线程中查询它们。
这样的问题是,当我在并行进行9个查询时进行更多查询时,它开始抛出异常System.InvalidOperationException : Invalid attempt to call CheckDataIsReady when reader is closed
:
[Fact]
public void TestMemory()
{
const string key = "myKey1";
//re-creating record with Value = large 30mb string
using (var db = new MyDbContext())
{
var existingRecord = db.CachedValues.FirstOrDefault(e => e.Key == key);
if (existingRecord!=null)
{
db.Remove(existingRecord);
db.SaveChanges();
}
var myHugeString = new string('*',30*1024*1024);
db.CachedValues.Add(new CachedValue() {Key = key, Value = myHugeString});
db.SaveChanges();
}
//Try to load this record in parallel threads, creating new dbContext
const int threads = 10;
Parallel.For(1, threads, new ParallelOptions(){MaxDegreeOfParallelism = threads}, (i) =>
{
using (var db = new MyDbContext())
{
var entity = db.CachedValues.FirstOrDefault(c => c.Key == key);
}
});
}
试图在每次读取DB之前/之后执行GC.Collect(); GC.WaitForFullGCComplete();
- 不帮助
试图通过sqlDataReader.ExecuteReader(CommandBehavior.SequentialAccess)
直接在较低层次上模仿这种行为 - 它抛出OutOfMemoryException
因此,在调查后,我发现这只是一个淘汰问题的问题,因为在分配30mb*2的第8个并联请求后(因为它是Unicode char),由.NET分配的内存数量实际上超过1.2GB在我的应用程序中,对于我的Workstation .NET运行时间就足够了,可以在缺乏记忆的情况下开始锁定(https://learn.microsoft.com/en-us/dotnet/dant/standard/standard/garbage-collection/garbage-collection/fundamentals#ephemeral-generations-and-generations-and-and-and-and-an--generations-and-an--generation - 段)。
这样说,我没有其他东西只是在重试读取(MEM分配)操作之前,直到它成功地强迫GC收集在捕获块中。
Retry.Action(() =>
{
using (var db = new MyDbContext())
{
var entity = db.CachedValues.FirstOrDefault(c => c.Key == key);
}
}).OnException((OutOfMemoryException exception) =>
{
GC.Collect();
GC.WaitForFullGCComplete();
return true;
})
.OnException((InvalidOperationException exception) =>
{
if (exception.Message != "Invalid attempt to call CheckDataIsReady when reader is closed.")
{
return false;
}
GC.Collect();
GC.WaitForFullGCComplete();
return true;
})
.Run();
如果您知道一些更好的解决方案