Laravel中的锁

昔日寒山问拾得曰:世间有人谤我、欺我、辱我、笑我、轻我、贱我、恶我、骗我、如何处治乎?拾得曰:只要忍他、让他、由他、避他、耐他、敬他、不要理他,再待几年你且看他。

查询构造器也包含一些可以帮助你在 select 语法上实现 「悲观锁定」的函数。

sharedLock

共享锁可防止选中的数据列被篡改,直到事务被提交为止

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

lockForUpdate

使用「更新」锁可避免行被其它共享锁修改或选取

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

相同和不同

  • 相同的地方是都能避免同一行数据被其他 transaction 进行 update。
  • 不同的地方是:
    sharedLock 不会阻止其他 transaction 读取同一行
    lockForUpdate 会阻止其他 transaction 读取同一行(需要特别注意的是,普通的非锁定读取读取依然可以读取到该行,只有 sharedLock 和 lockForUpdate 的读取会被阻止。)

试验

事务本身并不会锁表或者锁行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 
Route::get('/haha', function () {
\DB::beginTransaction();
\App\Models\User::query()->where('id', 13)->first();
sleep(5);
\DB::commit();
});

// hehe的查询操作并不会受到影响
Route::get('/hehe', function () {
$user = \App\Models\User::query()->where('id', 13)->first();
return $user;
});
// 更新也没有任何影响

使用共享锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// sharedLock
Route::get('/haha', function () {
\DB::beginTransaction();
\App\Models\User::query()->where('id', 13)->sharedLock()->first();
sleep(5);
\DB::commit();
});

// 普通的查询没有影响
Route::get('/hehe', function () {
$user = \App\Models\User::query()->where('id', 13)->first();
return $user;
});
// 同样使用共享锁的查询没有影响
Route::get('/hehe', function () {
$user = \App\Models\User::query()->where('id', 13)->sharedLock()->first();
return $user;
});
// 使用lockForUpdate的查询会有影响

Route::get('/hehe', function () {
$user = \App\Models\User::query()->where('id', 13)->lockForUpdate()->first();
return $user;
});

使用排他锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Route::get('/haha', function () {
\DB::beginTransaction();
\App\Models\User::query()->where('id', 13)->lockForUpdate()->first();
sleep(5);
\DB::commit();
});

//普通的查询并没有影响
Route::get('/hehe', function () {
$user = \App\Models\User::query()->where('id', 13)->first();
return $user;
});

// 使用了共享锁或者排他锁的查询才会有影响
Route::get('/hehe', function () {
$user = \App\Models\User::query()->where('id', 13)->sharedLock()->first();
return $user;
});
// 对更新有影响
Route::get('/hehe', function () {
$user = \App\Models\User::query()->where('id', 13)->update(['name' => 'yangzie']);
return $user;
});