Kri*_*ofe 20 php database transactions table-locking laravel
我使用laravel5.5的数据库事务进行在线支付应用程序.我有一个company_account表来记录每次付款(type,amount,create_at,gross_income).gross_income创建新记录时,我需要访问最后一条记录.所以我需要在具有读写表锁的事务时锁定表,以避免同时进行多次付款.
我已经参考了laravel的doc,但我不确定该事务是否会锁定表.如果事务将锁定表,那么什么是锁类型(读锁定,写锁定或两者)?
DB::transaction(function () {
// create company_account record
// create use_account record
}, 5);
Run Code Online (Sandbox Code Playgroud)
码:
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);
Run Code Online (Sandbox Code Playgroud)
由于您要更新2个表,因此仍需要使用事务来保持同步更改.请考虑以下代码:
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);
Run Code Online (Sandbox Code Playgroud)
有2个原子查询.第一个将记录插入user_account表中,另一个将记录插入表中tiger_account.
如果这两个查询之间发生了可怕的事情,您需要该事务以保证不会应用任何更改.可怕的事情不是并发请求,而是php应用程序,网络分区或其他任何阻止执行第二个查询的事件的突然死亡.在这种情况下,第一个查询的更改将回滚,因此数据库保持一致状态.
这两个查询都是原子的,这可以保证每个查询中的数学运算是孤立完成的,此时没有其他查询更改表.说2个并发请求可能同时为同一个用户处理2个付款.第一个将插入或更新user_account表中的记录,第二个查询将更新记录,两者都将添加记录tiger_account,并且每个事务提交时所有更改将在db中永久设置.
我做的几个假设:
user_id是user_account表中的主键.tiger_account.$old_tiger_account在OP代码中调用的那个,因为当数据库中没有任何内容时,不清楚预期的行为是什么.一句警告.这些是原始查询.您应该特别注意将来重构模型,并编写更多集成测试,因为一些应用程序逻辑从命令式PHP转移到声明式SQL.我相信保证没有竞争条件是合理的价格,但我想说清楚它不是免费的.
我遇到了这个答案的问题MySQL的:交易VS锁定表,这说明交易和锁定表。它显示了应在此处使用的事务和锁定。
我参考了Laravel lockforupdate(悲观锁定)和如何将参数传递给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);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
11013 次 |
| 最近记录: |