在我的日志中,我看到在负载测试期间记录了一些 System.OutOfMemoryException 错误。它们来自 Dapper 查询。根据堆栈跟踪,调整列表大小时会发生错误。
System.OutOfMemoryException:引发了“System.OutOfMemoryException”类型的异常。 在 System.Collections.Generic.List
1.AddWithResize(T 项目) 在 C:\projects\dapper\Dapper\SqlMapper.Async.cs:line 4421.set_Capacity(Int32 value) at System.Collections.Generic.List
const string sql = $@"
SELECT [Column 1]
,[Column 2]
,[Column 3]
,[Column 4]
,[Column 5]
,[Column 6]
,[Column 7]
,[Column 8]
,[Column 9]
,[Column 10]
,[Column 11]
,[Column 12]
FROM [MyDb].[MyTable]
WHERE co = @CompanyId AND process = @Process
";
await using var connection = _dbConnectionProvider.Create(DbKey.MyDb);
var parameters = new { CompanyId = CompanyDbString(companyId), Process = process };
var command = new CommandDefinition(sql, parameters);
var results = await connection.QueryAsync<MyEntity>(command); // error happens here
return results;
我查看了数据库,发现有问题的数据大小为 141,846 行。这些列没有什么“不寻常的”(没有巨大的文本斑点或类似的东西),只是一些合理的 varchars、datetimes、ints 等。所有 varchars 最多 100 个字符。
只是为了避免不可避免的“你真的需要加载所有数据吗?”萌芽中的评论:是的,我愿意。
所以,加载的数据是一个相当大的数据集,但没有什么太疯狂的。我很惊讶它内存不足。我发现特别有趣的是异常源于
System.Collections.Generic.List1.set_Capacity(Int32 value)
。基于此,看起来错误发生在列表调整其内部数组大小时。
关于如何解决这个问题,我的第一个想法是对结果进行分页,这是这里的建议:https://stackoverflow.com/a/52212051
但是,我不确定这是否可行,因为它仍然依赖于添加到的列表。这是一个合理的担忧吗?如果是这样,首先获取计数并以此作为初始容量开始列表是不是一个好方法?
或者是否有一个不同的集合比 List 更适合调整大小/内存?
你需要做一个无缓冲的 Dapper 查询,并将结果插入到一个预先确定大小的列表中。
var list = new List<MyEntity>(someBigSizeHere);
var command = new CommandDefinition(sql, parameters) { Buffered = false };
using var results = await connection.QueryAsync<MyEntity>(command);
list.AddRange(results);
return list;
请注意,您必须确保在关闭连接之前完全阅读并处理
results
。
如果在此之后你仍然遇到内存不足的异常,那么你根本没有足够的数据。尝试编译为 64 位并向系统添加更多 RAM。或者更好:重新思考为什么需要这么多数据。