mysql - 超出 EF Core 异步存储过程锁定等待超时
问题描述
在我的上下文中,我正在使用 EF Core 异步运行 2 个存储过程。这导致我出现死锁和超时问题。
下面我展示了调用其他 2 个调用存储过程的方法的代码:
public PortfolioPublishJobStep...
private async Task DoExecuteAsync(ProcessingContext context)
{
var (startDate, endDate) = GetInterval(context);
var portfolioApiId = context.Message.ManagedPortfolioApiId;
using var transactionScope = TransactionScopeFactory.CreateTransactionScope(timeout: Timeout, transactionScopeAsyncFlowOption: TransactionScopeAsyncFlowOption.Enabled);
var asyncTasks = new List<Task>();
foreach (var publishableService in _publishableServices)
{
var asyncTask = publishableService.PublishAsync(portfolioApiId, startDate, endDate);
asyncTasks.Add(asyncTask);
}
await Task.WhenAll(asyncTasks.ToArray()).ConfigureAwait(continueOnCapturedContext: false);
transactionScope.Complete();
}
在下面调用它们各自过程的类/方法......
PortfolioFinancialBenchmarkDataService...
public async Task PublishAsync(string portfolioApiId, DateTime startDate, DateTime endDate)
{
if (string.IsNullOrWhiteSpace(portfolioApiId))
{
throw new ArgumentException(nameof(portfolioApiId));
}
var repository = UnitOfWork.Repository<PortfolioFinancialBenchmarkData>();
await repository.RemoveAsync(x => x.PortfolioApiId == portfolioApiId && x.ReferenceDate >= startDate && x.ReferenceDate <= endDate).ConfigureAwait(continueOnCapturedContext: false);
var parameters = new[]
{
DbParameterFactory.CreateDbParameter<MySqlParameter>("@PortfolioApiId", portfolioApiId),
DbParameterFactory.CreateDbParameter<MySqlParameter>("@StartDate", startDate),
DbParameterFactory.CreateDbParameter<MySqlParameter>("@EndDate", endDate)
};
await repository.ExecuteSqlCommandAsync("CALL PublishPortfolioFinancialBenchmarkData(@PortfolioApiId, @StartDate, @EndDate);", parameters).ConfigureAwait(continueOnCapturedContext: false);
}
和这个:
PortfolioFinancialDataService...
public async Task PublishAsync(string portfolioApiId, DateTime startDate, DateTime endDate)
{
if (string.IsNullOrWhiteSpace(portfolioApiId))
{
throw new ArgumentException(nameof(portfolioApiId));
}
var repository = UnitOfWork.Repository<PortfolioFinancialData>();
await repository.RemoveAsync(x => x.PortfolioApiId == portfolioApiId && x.ReferenceDate >= startDate && x.ReferenceDate <= endDate).ConfigureAwait(continueOnCapturedContext: false);
var parameters = new[]
{
DbParameterFactory.CreateDbParameter<MySqlParameter>("@PortfolioApiId", portfolioApiId),
DbParameterFactory.CreateDbParameter<MySqlParameter>("@StartDate", startDate),
DbParameterFactory.CreateDbParameter<MySqlParameter>("@EndDate", endDate)
};
await repository.ExecuteSqlCommandAsync("CALL PublishPortfolioFinancialData(@PortfolioApiId, @StartDate, @EndDate);", parameters).ConfigureAwait(continueOnCapturedContext: false);
}
我相信问题是同时连接到数据库。
正如我在其他地方看到的那样,我认为我使用 TransactionScopeAsyncFlowOption 缓解了这种情况,但问题仍然存在。
在过程执行期间,其中一个过程提供的表之一发生死锁,并出现超时错误。
和异常消息:
MySqlConnector.MySqlException (0x80004005): Lock wait timeout exceeded; try restarting transaction
在某些时候,我还收到以下消息:
MySqlConnector.MySqlException (0x80004005): XA_RBDEADLOCK: Transaction branch was rolled back: deadlock was detected
我进行的测试:
将数据库超时设置为 50 到 100 秒,失败
将 PortfolioPublishJobStep 超时设置为 2 到 3 分钟,将 PortfolioFinancialBenchmarkDataService 和 PortfolioFinancialDataService 设置为 1 到 2 分钟,失败
仅运行 2 个存储过程中的 1 个,成功
同步运行程序,成功
因此,我得出结论,问题可能出在 2 笔交易的开启中,我相信其中一笔交易可能正在等待另一笔交易的结束……
解决方案
Mysql导致锁的情况有很多。例如:
- 在不提交的情况下执行 DML 操作,然后执行删除操作将锁定表。
- 在同一事务中插入和更新同一条数据。
- 表索引设计不当导致数据库死锁。
- 长东西,阻塞DDL,然后阻塞同一张表的所有后续操作。
解决方案
- 应急方法:
- 显示完整的进程列表;killdrop 有问题的进程。
Show full processlist; kill x;
- 有时候通过processlist,是看不到哪里有锁在等待的。当两个事务处于提交阶段时,无法反映在进程列表中。
- 显示完整的进程列表;killdrop 有问题的进程。
- 治疗方法:
- 从innodb_trx中选择*;
- 检查哪些事务占用表资源。
- 通过这个方法,你需要对innodb有所了解才能处理。
- innodb_lock_wait_timeout:InnoDB的dml操作的行级锁的等待时间
- innodb_lock_wait_timeout 是指事务等待获取资源的最长时间。
- 如果超过这个时间没有分配资源,会返回申请失败;当锁等待超过设定的时间时,会报如下错误 ERROR 1205 (HY000): Lock wait timeout exceeded; 尝试重新启动事务;。
- 参数的时间单位是秒,最小值可以设置为1s(一般不会设置这么小),最大值可以设置为1073741824秒,默认安装值为50s(默认参数设置)。
- 如何修改innode lock wait timeout的值?
set innodb_lock_wait_timeout=100; set global innodb_lock_wait_timeout=100;
- 或者:修改参数文件/etc/my.cnf
innodb_lock_wait_timeout = 50
- innodb_lock_wait_timeout 是指事务等待获取资源的最长时间。
推荐阅读
- apollo - 如何使用
具有初始查询的组件? - typescript - 删除和删除有什么区别?
- mysql - Mysql 两个查询都使用“Using where & Using temporary”,但是速度有区别
- python - 如何切换 Jupyter Notebook 显示日志记录
- ios - 有没有办法以编程方式在选项卡栏上创建视图控制器?
- c# - 使用 Setter 反序列化计算属性
- django - 如何更改 bootstrap4 django-tables2 的标题
- c# - 通过 API 3.0 将订阅者添加到 Mailchimp
- c# - WPF 中的实时图表
- ruby - 将 Ruby 哈希 (key,value) 转换为单独的键