Laravel的lockforupdate(悲观锁定)

22

我正在尝试弄清楚如何正确地使用/测试lockforupdate,但我发现它并没有像我预期的那样起作用

这只是一个测试

public function index() {
        return dd(\DB::transaction(function() {
            if (\Auth::guard('user')->check()) {
                $model = \App\Models\User::find(1)->lockForUpdate();
                sleep(60);
                $model->point = 100000;
                $model->save();
            } else {
                $model = \App\Models\User::find(1);
                $model->point = 999;
                $model->save();
            }

            return $model;
        }));
}

我尝试在两个浏览器中测试,浏览器1的用户已登录而浏览器2未登录。当浏览器1点击“刷新”时,会出现锁定并在更新之前等待60秒。

在这60秒内,我去了浏览器2并点击了“刷新”,但是该记录没有被锁定,我检查了phpmyadmin并且记录已经更新(在浏览器1触发的60秒锁定范围内)。

但是在60秒后,记录再次被浏览器1修改(点100000)

那么我是否误解了lockForUpdate的用法?还是我的测试方法有问题?

我期望的是在第一个60秒内不应该由浏览器2修改行(空白页面带有加载图标或错误提示?)

https://laravel.com/docs/5.2/queries#pessimistic-locking

我做了一些研究,但仍无法理解sharedLock(LOCK IN SHARE MODE)和lockForUpdate(FOR UPDATE)之间的区别。

顺便说一句,我确认数据库是InnoDB。

3个回答

24

这项工作终于完成了,但仍然不理解 sharedLock (LOCK IN SHARE MODE) 和 lockForUpdate (FOR UPDATE) 的区别。

    public function index() {
        return dd(\DB::transaction(function() {
            if (\Auth::guard('user')->check()) {
                $model = \App\Models\User::lockForUpdate()->find(1);
                sleep(30);
                $model->point = 100000;
                $model->save();
            } else {
                $model = \App\Models\User::lockForUpdate()->find(1);
                $model->point = $model->point + 1;
                $model->save();
            }

            return $model;
        }));
    }

11
来自文档:https://laravel.com/docs/5.2/queries#pessimistic-lockingsharedLock 仅为写入操作加锁,lockForUpdate 还阻止了它们被选择。 - cdarken
事务锁用于处理多用户访问之间的数据库。如果您的表被锁定,则其他用户无法访问它。因此,数据得到了适当的管理。 - Shankar Thiyagaraajan

21

这虽然是一个旧问题,但我相信我的答案可以澄清->lockForUpdate()的工作方式

Laravel文档中的解释:

共享锁定会防止选择的行在您提交事务之前被修改。

因此如文档中所写,锁定将在调用它时生效,直到您的事务完成为止。

请记住:

->find(1) 的工作方式类似于 ->first()->get()->insert()->save() 等 - 它执行查询

->lockForUpdate() 的工作方式类似于 ->where()->select()join() 等 - 它添加到查询,但不执行它

$model = \App\Models\User::find(1)->lockForUpdate(); - 您尝试在查询已经执行之后添加锁定

$model = \App\Models\User::lockForUpdate()->find(1); - 您在查询执行之前添加了锁定,因此锁定在事务完成之前一直有效

区别在于,在第一种情况下,->lockForUpdate()没有执行,而您认为它已经执行了。


1
我认为这应该被接受为问题的答案。 - Александр Родригес

2

阅读本文 参考

Laravel中的悲观锁与乐观锁

  • SharedLock:

    DB::table('users')->where('votes', '>', 100)->sharedLock()->get();
    
  • LockForUpdate:

    DB::table('users')->where('votes', '>', 100)->lockForUpdate()->get();
    

Laravel 文档


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接