如果突然多个请求发送到我的数据库并同时更新,我想阻止我的数据库更新。
我以创建用户 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');
}
});