首页 > 解决方案 > 并发请求中的 Laravel 数据库锁表

问题描述

如果突然多个请求发送到我的数据库并同时更新,我想阻止我的数据库更新。

我以创建用户 Laravel 用户表为例,使用 JMeter 模拟有两个用户,并发请求更改同一行数据。

例如场景

我的银行账户有 1000 美元。同时,有两个请求从我的账户向 A 先生和 B 先生转账。

我的余额记录是 1000,请求 1 - 向 Mr.A 发送 $700,请求 2 - 向 Mr.B 发送 $700。如果我没有锁桌,系统会认为我有足够的余额来汇款。

我的目标是当涉及到并发请求时,第二个(FIFO,即使它是并发 Web 服务器仍然会处理它并将其中一个视为第二个)将抛出异常/或向表示正在使用的请求显示返回错误.

下面是我的测试代码,我使用 JMeter 来运行测试。但是,测试表明两个请求都已更新。因此,最新的请求将覆盖第一个请求。

下面,我预计 Laravel 会为数据库锁定抛出异常或错误,但它仍然会继续进行。

Route::get('/db-a', function() {

    \DB::beginTransaction();

    //lock the table
    $rs = \DB::table('users')->where('id', '1')->lockForUpdate()->first();

    $sql = "update users set remember_token='this is a' where id=1";
    \DB::update(DB::raw($sql));

    //purposely put the sleep to see can the table / row be locked

    sleep(3);

    \DB::commit();

    $rs = DB::table('users')->where('id', '1')->first();
    echo($rs->remember_token);
    return;
});

Route::get('/db-b', function () {

    \DB::beginTransaction();

    //lock the table
    $rs = \DB::table('users')->where('id', '1')->lockForUpdate()->first();

    $sql = "update users set remember_token='this is b' where id=1";
    \DB::update(DB::raw($sql));

    //purposely put the sleep to see can the table / row be locked
    sleep(3);


    \DB::commit();

    $rs = DB::table('users')->where('id', '1')->first();
    echo($rs->remember_token);
    return;
});

我做了另一个版本来手动捕获异常,但也不起作用

Route::get('db-callback-a', function() {
    try {
        app('db')->transaction(function () {
            $record = \DB::table('users')->where('id', 1)->lockForUpdate()->first();
            $sql = "update users set remember_token='this is callback a' where id=1";

            \DB::update(DB::raw($sql));
            sleep(3);
        });
    } 
    catch (\Exception $e) {
        // display an error to user
        echo('Database Row in Use, update later');
    }
});

Route::get('db-callback-b', function () {
    try {

        app('db')->transaction(function () {
            $record = \DB::table('users')->where('id', 1)->lockForUpdate()->first();
            $sql = "update users set remember_token='this is callback b' where id=1";


            \DB::update(DB::raw($sql));
            sleep(3);
        });
    } catch (\Exception $e) {
        // display an error to user
        echo ('Database Row in Use, update later');
    }
});

标签: phpdatabaselaraveltable-lock

解决方案


推荐阅读