c# - Entity Framework 6 - 处理二进制数据时的内存和性能问题(异步方法)
问题描述
我们有一个旧的遗留系统在工作,它将二进制数据存储在我们的 SQL Server 中。当我们使用使用 EF6 的新 .NET 平台在表中查找数据时,内存会出现峰值,与(同步方法)FindAsync
相比,性能很糟糕Find
有谁知道解决此问题的解决方法或方法?我已经向 Microsoft 提出了一个问题,但是我们很着急,因为我们的客户在服务器上遇到 20 分钟的冻结。冻结的原因似乎是内存释放速度不够快,无法让垃圾收集清理内存,以及服务器太忙而无法足够快清理的其他情况。
我已经使用 EF 6 制作了一个快速的 .NET 4.8 项目来重现该错误。
using System;
using System.Data.Entity;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace PerfEFIssueFramework
{
public class Item
{
public int Id { get; set; }
public byte[] Data { get; set; }
}
public class ItemContext : DbContext
{
public DbSet<Item> Items { get; set; }
public ItemContext() : base(@"Data Source=localhost;Initial Catalog=ItemDb;Integrated Security=False;User ID=user;Password=pw")
{ }
}
internal class Program
{
private static async Task Main(string[] args)
{
Console.WriteLine("Ready to consume a lot of memory with EF.");
using (var db = new ItemContext())
{
db.Database.CreateIfNotExists();
//insert dummy record
if (db.Items.ToArray().Length == 0)
{
db.Items.Add(new Item { Data = new byte[20 * 1024 * 1024] });
db.Items.Add(new Item { Data = new byte[40 * 1024 * 1024] });
db.Items.Add(new Item { Data = new byte[60 * 1024 * 1024] });
db.Items.Add(new Item { Data = new byte[80 * 1024 * 1024] });
db.Items.Add(new Item { Data = new byte[100 * 1024 * 1024] });
await db.SaveChangesAsync();
}
}
// Find
for (int i = 1; i < 6; i++)
{
// Find sync - No performance issues
using (var db = new ItemContext())
{
var stopwatch = Stopwatch.StartNew();
Console.WriteLine("Find sync method doesn't have performance and memory issue");
var item = db.Items.Find(i);
Console.WriteLine($"Record with id '{item.Id}' was fetched in {stopwatch.ElapsedMilliseconds}ms. Press any key to read again...");
}
// Find async - performance issues
using (var db = new ItemContext())
{
var stopwatch = Stopwatch.StartNew();
Console.WriteLine("Reproduce FindAsync performance and memory issue:");
var item = await db.Items.FindAsync(i);
Console.WriteLine($"Record with id '{item.Id}' was fetched in {stopwatch.ElapsedMilliseconds}ms. Press any key to read again...");
}
}
using (var db = new ItemContext())
{
db.Database.Delete();
}
}
}
}
性能问题
ID 1 = 20mb
ID 2 = 40mb
ID 3 = 60mb
ID 4 = 80mb
ID 5 = 100mb
我们可以清楚地看到 find 不运行 async 方法,它需要 150 到 350ms,但 async 需要 13000ms 到 280000ms
内存问题
- 使用 2 mb 二进制数据 Find 使用大约 52 mb
使用 2 mb 二进制数据 FindAsync 使用大约 96 mb
使用 20 mb 二进制数据 Find 使用大约 63 mb
- 对于 20 mb 二进制数据,FindAsync 使用大约 432 mb
(我们使用 EF Core 发现了同样的问题)
解决方案
要将 EF 与具有非常大 blob 的表一起使用,您应该使用Table Splitting,并且仅根据需要获取带有 blob 的实体。
或者,您可以通过删除 byte[] 实体属性并将其替换为 DbContext 上的流式访问方法来避免将 blob 数据加载到内存中,如下所示:
public Stream GetItemData(int Id)
{
var con = (SqlConnection)Database.Connection;
if (con.State != System.Data.ConnectionState.Open)
con.Open();
var cmd = con.CreateCommand();
cmd.CommandText = "select data from Items where id = @id";
cmd.Parameters.Add(new SqlParameter("@id", Id));
var rdr = cmd.ExecuteReader(System.Data.CommandBehavior.SequentialAccess);
var stream = rdr.GetStream(0);
return stream;
}
推荐阅读
- html - CakePHP - 在 CakePHP 表单帮助链接中添加 HTML 内容的最佳方法?
- sql - UPDATE table_name SET col_name = varchar WHERE col_name 为 NULL;
- javascript - 如何让 HTML 对象上的事件侦听器与 React 相处?
- angular - 等到两个 Observable 完成
- python - Tkinter Pyimage37 不存在
- python - Python:如何确保我的文本日期时间将成功转换为相同的格式
- python - 使用 python 关闭 PDF
- azure - 使用 Azure AAD 进行身份验证
- c# - 任务执行期间未显示 WPF 材料设计进度对话框
- python - 从子类调用时出现 AttributeError