【发布时间】:2021-08-08 10:41:07
【问题描述】:
我的应用程序是用 php/laravel 和 MySQL 数据库实现的。在这个系统中,用户可以将钱存入钱包。我在 users 表中有一个 wallet_balance 列。当用户将资金存入他们的钱包时,我将该列更新如下:
public function topup(Request $request)
{
$user = auth()->user();
$deposit_amount = $request->deposit_amount;
//e.g.
//$deposit_amount = 100
//$user->wallet_balance = 50
$user->wallet_balance = $user->wallet_balance + $deposit_amount;
$user->save();
// After saving I expect $user->wallet_balance to be 150 which works perfectly.
}
有些服务需要从用户的钱包中扣除钱款(在另一个功能中)。例如:
public function chargeService(Request $request)
{
$user = User::findOrFail($request->user_id);
$service_fee = $request->service_fee;
//$service_fee = 30
//$user->wallet_balance = 150
$user->wallet_balance = $user->wallet_balance - $service_fee;
$user->save();
// After saving I expect $user->wallet_balance to be 120 which works perfectly.
}
两次交易后,用户的钱包余额应该是 120。但是,在极少数情况下,两次交易可能会同时发生。这意味着在存款交易中,初始钱包余额为 50,对于服务费交易,初始钱包余额为 50(因为在两者中的任何一个更新钱包余额之前,他们同时查询了数据库)。这就是危险。第一笔交易的钱包余额为 150 (50 + 100),第二笔交易的钱包余额为 20 (50 - 30)。这会使用户的钱包余额为 150 或 20,具体取决于最后更新用户钱包余额的操作。
现在,我的问题是,我该如何处理我的钱包系统以避免这个臭名昭著的问题。我会非常感谢你们。
您可以建议更好的方式来表达问题。
【问题讨论】:
-
你看过数据库事务吗?
-
他们能帮助解决这个问题吗? @Aless55 让我看看。如果您有资源丰富的链接,请放在这里,我将不胜感激。
-
事实证明数据库事务在这种情况下无济于事。这是因为操作(钱包存款和服务收费)是两个完全不同的实例。服务收费由系统触发,钱包充值由用户触发。