间歇性锁定等待超时 Laravel 数据库事务(重试 5 次)
Intermittent Lock Wait Timeout Laravel DB Transaction (with 5 retries)
我们一直遇到间歇性锁定超时错误(大约每天 1-2 次,共约 250 次)。
结帐时,我们会获取所有用户详细信息、保存订单、处理任何付款,然后更新订单。我认为可能是二次更新导致的。
我们的代码示例(不完全相同但足够接近):
DB::transaction(function () use ($paymentMethod, $singleUseTokenId, $requiresPayment, $chargeAccount) {
// create order locally
$order = Order::create([
'blah' => $data['blah'],
]);
// handle payment
$this->handlePayment();
// update order with new status (with a secondary transaction for safety)
DB::transaction(function () use ($order) {
$order->update([
'status' => 'new status',
]);
}, 5);
}, 5); // Retry transaction 5 times - this reduced the lock timeout errors a lot
我们得到的间歇性错误是(实际值已删除):
SQLSTATE[HY000]: General error: 1205 Lock wait timeout exceeded; try restarting transaction (SQL: insert into `orders` (`user_id`, `customer_uuid`, `type_uuid`, `status_uuid`, `po_number`, `order_details`, `cart_identifier`, `cart_content`, `cart_sub_total`, `cart_tax`, `cart_grand_total`, `payment_type_uuid`, `shipping_address`, `uuid`, `updated_at`, `created_at`)
我已经阅读了很多,有人说增加超时(似乎是一种解决方法)、乐观锁定(我认为事务已经这样做了)和其他事情。
据我从数据库面包屑中可以看出,订单创建有时需要很长时间(例如,在 3 秒看到一个,另一个在 23 秒看到,由于某种原因通常是 50 毫秒插入),然后其他事情发生了,它尝试更新订单,但该行仍被 create() 锁定。
备注:
- 我们在订单上有 4 个外键 table(客户 uuid、用户 uuid、订单类型 uuid、订单状态 uuid)- 我觉得这些可能会导致问题。
- 一些 eloquent 创造了 3 秒,其他的 23 秒(只检查问题)。在大多数订单上,请求最多为 500 毫秒,因此这些是异常值。
有什么建议吗?
解决:订单uuid没有主键。非常愚蠢的错误。导致 InnoDB 基本上为索引创建一个 6 字节的键。并锁定连续插入,然后更新..
如果您看到 "lock wait timeout" 个错误,请查看 其他 个交易。特别有害的是长 运行 交易。您可以在 SHOW ENGINE INNODB STATUS\G
中找到它们。攀登 InnoDB 历史列表表明也有一些。目前 运行 长交易将在 information_schema.INNODB_TRX
.
中列出
请注意,如果事务获取了独占锁,则直到事务结束才会释放它,而不是查询结束。
首先,排除长 运行 查询。例如,慢速 UPDATE 将为其执行时间持有锁。
在相当快地完成所有查询后,请检查您的交易。使它们尽可能短。客户经常打开一个事务,执行一两个查询,然后转到第三方 API 调用或做其他繁重的工作并保持事务打开。与此同时,其他交易将获得 "Lock wait timeout".
我们一直遇到间歇性锁定超时错误(大约每天 1-2 次,共约 250 次)。
结帐时,我们会获取所有用户详细信息、保存订单、处理任何付款,然后更新订单。我认为可能是二次更新导致的。
我们的代码示例(不完全相同但足够接近):
DB::transaction(function () use ($paymentMethod, $singleUseTokenId, $requiresPayment, $chargeAccount) {
// create order locally
$order = Order::create([
'blah' => $data['blah'],
]);
// handle payment
$this->handlePayment();
// update order with new status (with a secondary transaction for safety)
DB::transaction(function () use ($order) {
$order->update([
'status' => 'new status',
]);
}, 5);
}, 5); // Retry transaction 5 times - this reduced the lock timeout errors a lot
我们得到的间歇性错误是(实际值已删除):
SQLSTATE[HY000]: General error: 1205 Lock wait timeout exceeded; try restarting transaction (SQL: insert into `orders` (`user_id`, `customer_uuid`, `type_uuid`, `status_uuid`, `po_number`, `order_details`, `cart_identifier`, `cart_content`, `cart_sub_total`, `cart_tax`, `cart_grand_total`, `payment_type_uuid`, `shipping_address`, `uuid`, `updated_at`, `created_at`)
我已经阅读了很多,有人说增加超时(似乎是一种解决方法)、乐观锁定(我认为事务已经这样做了)和其他事情。
据我从数据库面包屑中可以看出,订单创建有时需要很长时间(例如,在 3 秒看到一个,另一个在 23 秒看到,由于某种原因通常是 50 毫秒插入),然后其他事情发生了,它尝试更新订单,但该行仍被 create() 锁定。
备注:
- 我们在订单上有 4 个外键 table(客户 uuid、用户 uuid、订单类型 uuid、订单状态 uuid)- 我觉得这些可能会导致问题。
- 一些 eloquent 创造了 3 秒,其他的 23 秒(只检查问题)。在大多数订单上,请求最多为 500 毫秒,因此这些是异常值。
有什么建议吗?
解决:订单uuid没有主键。非常愚蠢的错误。导致 InnoDB 基本上为索引创建一个 6 字节的键。并锁定连续插入,然后更新..
如果您看到 "lock wait timeout" 个错误,请查看 其他 个交易。特别有害的是长 运行 交易。您可以在 SHOW ENGINE INNODB STATUS\G
中找到它们。攀登 InnoDB 历史列表表明也有一些。目前 运行 长交易将在 information_schema.INNODB_TRX
.
请注意,如果事务获取了独占锁,则直到事务结束才会释放它,而不是查询结束。
首先,排除长 运行 查询。例如,慢速 UPDATE 将为其执行时间持有锁。
在相当快地完成所有查询后,请检查您的交易。使它们尽可能短。客户经常打开一个事务,执行一两个查询,然后转到第三方 API 调用或做其他繁重的工作并保持事务打开。与此同时,其他交易将获得 "Lock wait timeout".