【发布时间】:2021-06-25 19:55:59
【问题描述】:
我有一个每 7 天运行一次的后台任务(由 IHostedService 启动),该任务将数据复制到同一个表中,仅更改 PK 以生成虚拟数据(用于演示)。
问题在于,当它尝试为第二个表保存新数据(大约 2k 条新记录)时,实体框架永远不会完成 SaveChangesAsync,此过程被阻止并开始消耗整个可用 RAM。
注意:第一个表有时会复制超过 2 万条新记录。
这是我当前的代码,我正在使用 Entity Framework Core 5.0.4 和 .NET Core 3.1:
using (var context = _context.CreateNewInstance())
{
var existingStudents = context.Students.Where(s => s.UniversityId == _destUniversity.Id);
var sourceStudents = context.Students.Where(s => s.UniversityId == _sourceUniversity.Id)
.Select(s => new Student()
{
//...properties
});
var newStudents = sourceStudents.Where(s => !existingStudents.Any(es => es.DiffKey == s.DiffKey)).ToArray();
if (newStudents.Length == 0)
{
return;
}
await context.Students.AddRangeAsync(newStudents);
await context.SaveChangesAsync(_cancellationToken.Token);
}
我已经尝试了所有这些:
- Disabling "Auto Detect Changes"
- Batch insert:这只适用于第一批。
- Use
IEnumerableinstead ofIQueryable - 任务中所有表的单一上下文
- 每个表的新上下文实例
- 比较数据库/表的配置并复制到本地
我有 2 个服务器(1 个本地,1 个远程)和 3 个数据库(1 个本地,2 个远程),代码适用于三个数据库中的两个(1 个本地和 1 个远程)。
我使用这段代码在 SQL 上获取任务的spid:
int psid;
using (var command = contextTmp.Database.GetDbConnection().CreateCommand())
{
command.CommandText = "select @@spid as id";
contextTmp.Database.OpenConnection();
using (var result = command.ExecuteReader())
{
result.Read();
psid = result.GetInt16("id");
}
contextTmp.Database.CloseConnection();
}
使用之前代码中的spid,我监控了SQL Server 端的请求以检查其状态:
select session_id,
status,
command,
blocking_session_id,
wait_type,
wait_time,
last_wait_type,
wait_resource
from sys.dm_exec_requests
where session_id = @id
结果:
| session_id | status | command | blocking_session_id | wait_type | wait_time | last_wait_type | wait_resource |
|---|---|---|---|---|---|---|---|
| 84 | suspended | SELECT | 0 | ASYNC_NETWORK_IO | 35 | ASYNC_NETWORK_IO |
我已经阅读了有关此问题的博客,但我还不明白,我想我错过了一些东西。我没有主意了。
其他来源:
- DBA stackexchange
- Understanding how SQL Server executes a query
- SQL Server ASYNC Network IO Wait Type
- Database Connection Hazards with Entity Framework
更新(1):
在我们从 .NET Core 2.2 更新到 3.1 之前,所有功能都运行良好。升级后我们开始看到奇怪的行为。
有什么建议吗?
【问题讨论】:
-
仅供参考 - 这不是死锁。直接挡住。通过回滚一个进程,死锁将很快得到解决。
-
好点,谢谢。那么,它是什么?
-
正如我所说,这是直接阻塞。
-
哦,好吧,这对我来说是新的,让我读一些关于它的东西
-
我认为关键在于批量工作,第一次。任何尝试都可能执行单个存储的命令?该方法可能在执行后没有正确“关闭”,问题是实体框架仍然认为该表正在处理,从而阻止另一个异步进程进行更改。尝试在没有异步的情况下进行存储,看看这是否不能解决您的问题,然后您就可以确定是否可以。
标签: c# sql-server .net-core entity-framework-core