我遇到了死锁问题,出现了以下异常:
System.InvalidOperationException: 出现了一个异常,可能是由于短暂故障引起的。请考虑通过在 'UseSqlServer' 调用中添加 'EnableRetryOnFailure()' 来启用短暂错误恢复。
为了复制问题,我甚至创建了第二个最简单的项目,只有几行代码,但也会出现相同的问题。它非常基础,只需要有两个引用不同实体的实体即可。在我看来,这是一种很常见的情况,EF Core 默认无法处理这种情况,并建议使用 EnableRetryOnFailure()
。
我的问题是:如果我们有一个大型应用程序,那么每秒钟可能有1000个用户触发重试,具体取决于他们使用会导致此类死锁的操作有多频繁。 这不会影响性能吗?这不会最终阻塞数据库吗?
重新创建此问题的简单代码:
这只是 API,所有代码都在控制器内部,以简化问题。数据库是由 EF Core(Code First 方法)生成的。它有两个控制器,具有接受包含要添加到数据库中的数据的 JSON 文件的端点。每当从文件添加数据时,都会保存有关文件的信息(文件名、日期和时间)。提交一个文件会导致许多带有数据的记录(取决于其中包含的内容)和一个带有文件名、日期和时间的记录(每个数据记录都具有相同的文件引用,因此所有记录都将具有相同的 InputFileId)。
您只需要同时向两个端点发送请求。或只需同时发送例如4个请求到同一个端点。我有2000个条目在 JSON 文件中,所以可能需要更长时间才能更容易地触发死锁。
用户控制器:
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
public MyDbContext DbContext { get; }
public UsersController(MyDbContext dbContext)
{
DbContext = dbContext;
}
[HttpPost]
public async Task<IActionResult> Import([FromForm]IFormFile file)
{
using var streamReader = new StreamReader(file.OpenReadStream());
JsonSerializer serializer = new JsonSerializer();
List<User> users = (List<User>)serializer.Deserialize(streamReader, typeof(List<User>));
var inputFile = new InputFile
{
FileName = file.FileName,
DateAdded = DateTime.Now
};
foreach (var user in users)
{
user.InputFile = inputFile;
}
await DbContext.AddRangeAsync(users);
await DbContext.SaveChangesAsync();
return Ok();
}
}
产品控制器:
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
public MyDbContext DbContext { get; }
public ProductsController(MyDbContext dbContext)
{
DbContext = dbContext;
}
[HttpPost]
public async Task<IActionResult> Import([FromForm]IFormFile file)
{
using var streamReader = new StreamReader(file.OpenReadStream());
JsonSerializer serializer = new JsonSerializer();
List<Product> products = (List<Product>)serializer.Deserialize(streamReader, typeof(List<Product>));
var inputFile = new InputFile
{
FileName = file.FileName,
DateAdded = DateTime.Now
};
foreach (var product in products)
{
product.InputFile = inputFile;
}
await DbContext.AddRangeAsync(products);
await DbContext.SaveChangesAsync();
return Ok();
}
}
Db上下文:
public class MyDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<InputFile> InputFiles { get; set; }
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
{
}
}
产品:
public class Product
{
public long Id { get; set; }
public string SerialNumber { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public long InputFileId { get; set; }
public InputFile InputFile { get; set; }
}
用户:
public class User
{
public long Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public long InputFileId { get; set; }
public InputFile InputFile { get; set; }
}
输入文件:
public class InputFile
{
public long Id { get; set; }
public string FileName { get; set; }
public DateTime DateAdded { get; set; }
}
Startup.cs,ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}