昔日寒山问拾得曰:世间有人谤我、欺我、辱我、笑我、轻我、贱我、恶我、骗我、如何处治乎?拾得曰:只要忍他、让他、由他、避他、耐他、敬他、不要理他,再待几年你且看他。
查询构造器也包含一些可以帮助你在 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(); });
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
| 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; });
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; });
|