首页 > 解决方案 > 间歇性锁定等待超时 Laravel DB 事务(重试 5 次)

问题描述

我们一直在遇到间歇性锁定超时错误(大约每天 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() 锁定。

笔记:

有什么建议么?

解决方法:订单uuid上没有主键。非常愚蠢的错误。导致 InnoDB 基本上为索引创建了一个 6 字节的键。并从连续插入锁定,然后更新..

标签: mysqllaraveltimeoutlockinginnodb

解决方案


如果您看到“锁定等待超时”错误,请查看其他事务。特别有害的是长期运行的事务。你可以在SHOW ENGINE INNODB STATUS\G. 攀登 InnoDB 历史列表表明也有。当前运行的长事务将列在information_schema.INNODB_TRX.

请注意,如果事务抓住了排他锁,它直到事务结束才被释放,而不是查询结束。

首先,排除长时间运行的查询。例如,慢速 UPDATE 将为其执行时间持有一个锁。

在所有查询都以相当快的速度进行后,请检查您的交易。使它们尽可能短。客户通常会打开一个事务,执行一两个查询,然后转到第三方 API 调用或执行其他繁重的工作并保持事务处于打开状态。同时其他事务将获得“锁定等待超时”。


推荐阅读