在 Laravel 中处理并发

问题描述 投票:0回答:3

我正在创建一个与 MySQL 库存数据库交互的 API。我们有 15 个用户可以预订产品,通过以下方式更新数据库:

  • 降低产品的
    on-hand
    值并增加产品的
    reserved
    值。

inventory
表看起来像这样:

id        int
sku       varchar
on-hand   int
reserved  int

问题是:如果2个用户尝试同时更新该行,如何处理该行的更新?

我想到的第一个方法是使用交易

<?php
    function reserveStock()
    {
        $db->beginTransaction();

        // SELECT on-hand, reserved from inventory

        // Update inventory values

        $db->commit();

        return response()->json([ 'success' => 1, 'data' => $data ])
    }

第二个是使用悲观锁定

<?php
    function reserveStock()
    {

        // SELECT on-hand, reserved from inventory with ->sharedLock()

        // Update inventory values

        return response()->json([ 'success' => 1, 'data' => $data ])
    }

第三个是创建一个值为 cero 的

updating
字段。选择要更新的产品时,我会在对这些行执行任何操作之前检查
updating
字段。我在这里看到的问题是我必须用
updating != 0
循环这些直到它们变得可用。更多选择和更新来自他的方法。

哪种行动方案最好?可能有比我在这里写的更多的选择。

php mysql laravel concurrency
3个回答
0
投票

既不要使用事务,也不要使用悲观锁定。

也不要使用更新。

对于赛车状况,你最好检查你的数据库,并摆脱更新,使用插入代替。制作一个数据透视表来容纳用户和产品之间的连接。并仅使第一个连接记录(其中 id 较小)成为实际记录。

如果需要更多解释,这是一个示例

假设 2 个用户竞相获取该产品。他们几乎同时在数据透视表中创建记录,但应该有人是第一个,对吧?他们都提交了非常小的事务来保存他们的数据。然后 - 他们都读取数据透视表来验证谁成功了。第一条记录对于它们来说是相同的,在使用中不会出现阻塞(至少是明确的)。因此,一个客户将获得他的记录并感到高兴,另一个客户将重新申请获得另一种产品。

问题解决了。


0
投票

我知道我迟到了,但我可以帮助像我这样遇到类似问题的人。 您可以通过使用作业/队列来解决这个问题。这样,与某个用户相关的“事务”就不会与另一个用户“同时”运行。考虑查看这篇关于并发攻击的文章,我相信您会欣赏的。


0
投票
try {
    // Start a transaction
    $mysqli->begin_transaction();

    // Fetch the record with a lock for update
    $productId = 1; // Replace with the actual product ID
    $result = $mysqli->query("SELECT * FROM inventory WHERE product_id = $productId FOR UPDATE");

    if (!$result) {
        // Handle query error
        throw new Exception("Error fetching record for update");
    }

    // Fetch the data from the result set
    $inventory = $result->fetch_assoc();

    if (!$inventory) {
        // Handle product not found
        throw new Exception("Product not found");
    }

    // Perform checks and update the record
    // ...

    // Commit the transaction
    $mysqli->commit();

    echo "Product updated successfully";
} catch (Exception $e) {
    // Rollback the transaction on error
    $mysqli->rollback();

    // Handle the exception, log it, or return an appropriate response
    echo "Failed to update product: " . $e->getMessage();
} finally {
    // Close the MySQLi connection
    $mysqli->close();
}`enter code here`
© www.soinside.com 2019 - 2024. All rights reserved.