mysql - 如何使用重试事务解决由并行 goroutine 引起的数据库死锁问题?
问题描述
我们正在努力使用 Go 建立一个交易所。现在的系统中有以下组件:
- 前端
- 后端
- 匹配引擎。
以下是系统的工作原理:
- 用户创建一个发往后端的订单。
- 后端按以下方式处理新订单:
- 它验证订单请求数据,然后创建一个数据库事务以更新帐户表中的用户帐户余额,并在订单表中插入新订单。
- db交互完成后;后端在队列中生成新的订单消息。
- 匹配引擎处理来自队列的新订单并将引擎响应(即更新的订单和交易)推送到另一个队列中
后端使用引擎响应如下:
- 多个 goroutine 并行运行以处理引擎响应并将接收到的更新存储在引擎响应中。
- 为了存储更新,在每个 goroutine 中创建一个数据库事务来更新用户账户余额、订单和存储新交易。
当并发(并行)事务尝试一次更新相同的用户帐户时,此阶段会出现问题,由于多个事务尝试锁定相同的记录,因此会在 DB 中创建死锁。此外,当用户先前下的订单正在匹配和处理时,用户可以创建新订单,如步骤 2 中所述,因此在步骤 2 中创建的事务也尝试更新后端访问的数据库中的相同用户帐户以存储匹配引擎响应. 这也增加了死锁的可能性。
如何正确管理和防止上述流程中产生的数据库死锁。
我们已尝试通过以下代码重试事务的解决方案,但仍然可以重现死锁问题
// Update accounts, orders, trades with max retry of 5
for i := 0; i < 5; i++ {
err = nil
time.Sleep(10 * time.Second)
if err = manager.saveEngineResponse(newEngineResponse, accountUpdatesToDoRef, &updatedOrders); err == nil {
break
}
logger.Debug("Error saving engine response", err)
}
if err != nil {
logger.Fatal(err)
return
}
错误:错误 1213:尝试获取锁时发现死锁;尝试重启事务
我参考了链接:How to Avoid mysql 'Deadlock found when trying to get lock; 尝试重新启动事务' ,它指出通过保持特定顺序的操作可以避免死锁。在上述情况下,所有交易都将首先更新账户,然后下单,如果有的话再交易。所以在不同的事务中操作的顺序是一致的
解决方案
推荐阅读
- reactjs - 如何检查 fetch 是否通过,如果是,则显示它?反应原生
- keycloak - 如何读取 Keycloak 用户上次登录的事件
- javascript - 如何从 React App 打开我的静态/单个网站演示?
- sql - 优化查询设计 Teradata
- python - 在 Python (Pandas) 或 Pyspark 中创建单词及其位置
- java - 无法使用 Spring Boot 在 MySQL 数据库中自动创建表
- spring-boot - 带有构造函数注入的 Spring Boot @WebMvcTest 不起作用
- python - 将元组列表转换为字典
- javascript - ymaps__WEBPACK_IMPORTED_MODULE_8__.default.ready 不是函数
- python - Leetcode #77 组合 - 不确定这个语法有什么问题