请问laravel数据库事务锁table?
Will laravel database transaction lock table?
我使用laravel5.5的数据库交易进行在线支付申请。我有一个 company_account table 来记录每笔付款(type
、amount
、create_at
、gross_income
)。创建新记录时,我需要访问最后一条记录的 gross_income
。所以我需要在有读写table锁的交易时加锁table,避免同时支付多笔。
我参考了 laravel 的文档,但我不确定事务是否会锁定 table。如果事务将锁定 table,锁定类型是什么(读锁、写锁或两者)?
DB::transaction(function () {
// create company_account record
// create use_account record
}, 5);
代码:
DB::transaction(function ($model) use($model) {
$model = Model::find($order->product_id);
$user = $model->user;
// **update** use_account record
try {
$user_account = User_account::find($user->id);
} catch (Exception $e){
$user_account = new User_account;
$user_account->user_id = $user->id;
$user_account->earnings = 0;
$user_account->balance = 0;
}
$user_account->earnings += $order->fee * self::USER_COMMISION_RATIO;
$user_account->balance += $order->fee * self::USER_COMMISION_RATIO;
$user_account->save();
// **create** company_account record
$old_tiger_account = Tiger_account::latest('id')->first();
$tiger_account = new Tiger_account;
$tiger_account->type = 'model';
$tiger_account->order_id = $order->id;
$tiger_account->user_id = $user->id;
$tiger_account->profit = $order->fee;
$tiger_account->payment = 0;
$tiger_account->gross_income = $old_tiger_account-> gross_income + $order->fee;
$tiger_account->save();
}, 5);
参考文献:
How to pass parameter to Laravel DB::transaction()
由于您正在更新 2 tables,您仍然需要使用事务来保持更改同步。考虑以下代码:
DB::transaction(function () {
$model = Model::find($order->product_id);
$user = $model->user();
DB::insert("
insert into user_account (user_id, earnings, balance) values (?, ?, ?)
on duplicate key update
earnings = earnings + values(earnings),
balance = balance + values(balance)
", [$user->id, $order->fee * self::USER_COMMISION_RATIO, $order->fee * self::USER_COMMISION_RATIO]);
DB::insert(sprintf("
insert into tiger_account (`type`, order_id, user_id, profit, payment, gross_income)
select '%s' as `type`, %d as order_id, %d as user_id, %d as profit, %d as payment, gross_income + %d as gross_income
from tiger_account
order by id desc
limit 1
", "model", $order->id, $user->id, $order->fee, 0, $order->fee));
}, 5);
有 2 个原子查询。第一个将记录插入 user_account
table,另一个将记录插入 tiger_account
.
如果这两个查询之间发生了可怕的事情,您需要事务来保证不应用任何更改。可怕的不是并发请求,而是 php 应用程序、网络分区或任何其他阻止执行第二个查询的突然死亡。在这种情况下,从第一个查询回滚更改,因此数据库保持一致状态。
两个查询都是原子的,这保证了每个查询中的数学运算都是独立完成的,此时没有其他查询更改 table。说有可能2个并发请求同时为同一个用户处理2笔支付。第一个将插入或更新 user_account
table 中的记录,第二个查询将更新记录,两者都会将记录添加到 tiger_account
,所有更改将永久设置在提交每个事务时的数据库。
我做了几个假设:
user_id
是 user_account
table 中的主键。
tiger_account
中至少有 1 条记录。在 OP 代码中称为 $old_tiger_account
的那个,因为不清楚当数据库中没有任何内容时预期的行为是什么。
- 所有货币字段都是整数,而不是浮点数。
- 是MySQL数据库。我使用 MySQL 语法来说明该方法。其他 SQL 风格的语法可能略有不同。
- 原始查询中的所有 table 名称和列名称。不记得 illuminate 命名约定了。
一句警告。这些是原始查询。您应该在未来的重构模型上格外小心,并编写更多的集成测试,因为一些应用程序逻辑从命令式 PHP 转变为声明式 SQL。我相信保证没有竞争条件是一个公平的价格,但我想crystal明确表示它不是免费的。
在我看来,如果你分别计算每条记录的即时总收入,你甚至不需要锁定 table,你知道锁定 table将直接减慢您的网站。
DB::transaction(function () use($order) {
$model = Model::find($order->product_id);
$user = $model->user;
// **update** use_account record
try {
$user_account = User_account::find($user->id);
} catch (Exception $e){
$user_account = new User_account;
$user_account->user_id = $user->id;
$user_account->earnings = 0;
$user_account->balance = 0;
}
$user_account->earnings += $order->fee * self::USER_COMMISION_RATIO;
$user_account->balance += $order->fee * self::USER_COMMISION_RATIO;
$user_account->save();
// **create** company_account record
$tiger_account = Tiger_account::create([
'type' => 'model',
'order_id' => $order->id,
'user_id' => $user->id,
'profit' => $order->fee,
'payment' => 0,
]);
$tiger_account->update([
'gross_income' => Tiger_account::where('id', '<=', $tiger_account->id)->sum('fee'),
]);
});
我遇到了这个 answer of the question MySQL: Transactions vs Locking Tables,它解释了事务和锁定 table。它显示了这里应该使用事务和锁定。
我参考了 and How to pass parameter to Laravel DB::transaction(),然后得到下面的代码。
不知道实现的好不好,至少现在可以用了
DB::transaction(function ($order) use($order) {
if($order->product_name == 'model')
{
$model = Model::find($order->product_id);
$user = $model->user;
$user_account = User_account::where('user_id', $user->id)->lockForUpdate()->first();
if(!$user_account)
{
$user_account = new User_account;
$user_account->user_id = $user->id;
$user_account->earnings = 0;
$user_account->balance = 0;
}
$user_account->earnings += $order->fee * self::USER_COMMISION_RATIO;
$user_account->balance += $order->fee * self::USER_COMMISION_RATIO;
$user_account->save();
$old_tiger_account = Tiger_account::latest('id')->lockForUpdate()->first();
$tiger_account = new Tiger_account;
$tiger_account->type = 'model';
$tiger_account->order_id = $order->id;
$tiger_account->user_id = $user->id;
$tiger_account->profit = $order->fee;
$tiger_account->payment = 0;
if($old_tiger_account)
{
$tiger_account->gross_income = $old_tiger_account->gross_income + $order->fee;
} else{
$tiger_account->gross_income = $order->fee;
}
$tiger_account->save();
}
}, 3);
我使用laravel5.5的数据库交易进行在线支付申请。我有一个 company_account table 来记录每笔付款(type
、amount
、create_at
、gross_income
)。创建新记录时,我需要访问最后一条记录的 gross_income
。所以我需要在有读写table锁的交易时加锁table,避免同时支付多笔。
我参考了 laravel 的文档,但我不确定事务是否会锁定 table。如果事务将锁定 table,锁定类型是什么(读锁、写锁或两者)?
DB::transaction(function () {
// create company_account record
// create use_account record
}, 5);
代码:
DB::transaction(function ($model) use($model) {
$model = Model::find($order->product_id);
$user = $model->user;
// **update** use_account record
try {
$user_account = User_account::find($user->id);
} catch (Exception $e){
$user_account = new User_account;
$user_account->user_id = $user->id;
$user_account->earnings = 0;
$user_account->balance = 0;
}
$user_account->earnings += $order->fee * self::USER_COMMISION_RATIO;
$user_account->balance += $order->fee * self::USER_COMMISION_RATIO;
$user_account->save();
// **create** company_account record
$old_tiger_account = Tiger_account::latest('id')->first();
$tiger_account = new Tiger_account;
$tiger_account->type = 'model';
$tiger_account->order_id = $order->id;
$tiger_account->user_id = $user->id;
$tiger_account->profit = $order->fee;
$tiger_account->payment = 0;
$tiger_account->gross_income = $old_tiger_account-> gross_income + $order->fee;
$tiger_account->save();
}, 5);
参考文献:
How to pass parameter to Laravel DB::transaction()
由于您正在更新 2 tables,您仍然需要使用事务来保持更改同步。考虑以下代码:
DB::transaction(function () {
$model = Model::find($order->product_id);
$user = $model->user();
DB::insert("
insert into user_account (user_id, earnings, balance) values (?, ?, ?)
on duplicate key update
earnings = earnings + values(earnings),
balance = balance + values(balance)
", [$user->id, $order->fee * self::USER_COMMISION_RATIO, $order->fee * self::USER_COMMISION_RATIO]);
DB::insert(sprintf("
insert into tiger_account (`type`, order_id, user_id, profit, payment, gross_income)
select '%s' as `type`, %d as order_id, %d as user_id, %d as profit, %d as payment, gross_income + %d as gross_income
from tiger_account
order by id desc
limit 1
", "model", $order->id, $user->id, $order->fee, 0, $order->fee));
}, 5);
有 2 个原子查询。第一个将记录插入 user_account
table,另一个将记录插入 tiger_account
.
如果这两个查询之间发生了可怕的事情,您需要事务来保证不应用任何更改。可怕的不是并发请求,而是 php 应用程序、网络分区或任何其他阻止执行第二个查询的突然死亡。在这种情况下,从第一个查询回滚更改,因此数据库保持一致状态。
两个查询都是原子的,这保证了每个查询中的数学运算都是独立完成的,此时没有其他查询更改 table。说有可能2个并发请求同时为同一个用户处理2笔支付。第一个将插入或更新 user_account
table 中的记录,第二个查询将更新记录,两者都会将记录添加到 tiger_account
,所有更改将永久设置在提交每个事务时的数据库。
我做了几个假设:
user_id
是user_account
table 中的主键。tiger_account
中至少有 1 条记录。在 OP 代码中称为$old_tiger_account
的那个,因为不清楚当数据库中没有任何内容时预期的行为是什么。- 所有货币字段都是整数,而不是浮点数。
- 是MySQL数据库。我使用 MySQL 语法来说明该方法。其他 SQL 风格的语法可能略有不同。
- 原始查询中的所有 table 名称和列名称。不记得 illuminate 命名约定了。
一句警告。这些是原始查询。您应该在未来的重构模型上格外小心,并编写更多的集成测试,因为一些应用程序逻辑从命令式 PHP 转变为声明式 SQL。我相信保证没有竞争条件是一个公平的价格,但我想crystal明确表示它不是免费的。
在我看来,如果你分别计算每条记录的即时总收入,你甚至不需要锁定 table,你知道锁定 table将直接减慢您的网站。
DB::transaction(function () use($order) {
$model = Model::find($order->product_id);
$user = $model->user;
// **update** use_account record
try {
$user_account = User_account::find($user->id);
} catch (Exception $e){
$user_account = new User_account;
$user_account->user_id = $user->id;
$user_account->earnings = 0;
$user_account->balance = 0;
}
$user_account->earnings += $order->fee * self::USER_COMMISION_RATIO;
$user_account->balance += $order->fee * self::USER_COMMISION_RATIO;
$user_account->save();
// **create** company_account record
$tiger_account = Tiger_account::create([
'type' => 'model',
'order_id' => $order->id,
'user_id' => $user->id,
'profit' => $order->fee,
'payment' => 0,
]);
$tiger_account->update([
'gross_income' => Tiger_account::where('id', '<=', $tiger_account->id)->sum('fee'),
]);
});
我遇到了这个 answer of the question MySQL: Transactions vs Locking Tables,它解释了事务和锁定 table。它显示了这里应该使用事务和锁定。
我参考了
不知道实现的好不好,至少现在可以用了
DB::transaction(function ($order) use($order) {
if($order->product_name == 'model')
{
$model = Model::find($order->product_id);
$user = $model->user;
$user_account = User_account::where('user_id', $user->id)->lockForUpdate()->first();
if(!$user_account)
{
$user_account = new User_account;
$user_account->user_id = $user->id;
$user_account->earnings = 0;
$user_account->balance = 0;
}
$user_account->earnings += $order->fee * self::USER_COMMISION_RATIO;
$user_account->balance += $order->fee * self::USER_COMMISION_RATIO;
$user_account->save();
$old_tiger_account = Tiger_account::latest('id')->lockForUpdate()->first();
$tiger_account = new Tiger_account;
$tiger_account->type = 'model';
$tiger_account->order_id = $order->id;
$tiger_account->user_id = $user->id;
$tiger_account->profit = $order->fee;
$tiger_account->payment = 0;
if($old_tiger_account)
{
$tiger_account->gross_income = $old_tiger_account->gross_income + $order->fee;
} else{
$tiger_account->gross_income = $order->fee;
}
$tiger_account->save();
}
}, 3);