【问题标题】:Intermittent Lock Wait Timeout Laravel DB Transaction (with 5 retries)间歇性锁定等待超时 Laravel DB 事务(重试 5 次)
【发布时间】:2020-05-31 22:25:03
【问题描述】:

我们一直在遇到间歇性锁定超时错误(大约每天 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 个外键(客户 uuid、用户 uuid、订单类型 uuid、订单状态 uuid) - 我觉得这些可能会导致问题。
  • 一些 eloquent 创建需要 3 秒,其他 23 秒(仅检查问题)。大多数订单的请求时间最长为 500 毫秒,因此这些是异常值。

有什么建议吗?

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

【问题讨论】:

  • UUID 不连续;它们散落在各处。
  • @RickJames 你知道什么是 UUID 吗?我相信您将 v4 UUID 与顺序 ID 混淆了

标签: mysql laravel timeout locking innodb


【解决方案1】:

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

请注意,如果事务获取了独占锁,则直到事务结束才会释放,而不是查询结束。

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

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

【讨论】:

  • 感谢您的回复!最新的死锁(最后 3 个)显示了几乎相同的情况。 MySQL thread id 1334946, OS thread handle 46998731650816, query id 1308220069 XXXX XXXX updating update `orders` set `request_xml` = ?, `orders`.`updated_at` = ? where `uuid` = ? *** (2) HOLDS THE LOCK(S): RECORD LOCKS space id 364 page no 9 n bits 0 index GEN_CLUST_INDEX of table `XXXXX`.`orders` trx id 1328386487 lock_mode X
  • 从上面我可以看出,它表明在订单插入查询之后创建的 GEN_CLUST_INDEX 正在锁定该行,因此立即更新查询不能立即运行。这也是你从中收集到的吗?关于如何解决这个问题有什么想法吗?
  • 这看起来和插入一样的问题,然后在更新后直接插入,但没有说明如何解决,除非我将插入从事务中取出或其他什么...dba.stackexchange.com/questions/39550/…
  • 等等,uuid不是主键吗?应该是
  • 没有加起来。当用户未定义主键且没有唯一索引可用作主键时,InnoDB 从 6 字节内部整数字段创建 GEN_CLUST_INDEX。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-09
  • 2019-06-02
  • 1970-01-01
  • 2016-12-16
  • 1970-01-01
  • 2013-09-22
相关资源
最近更新 更多