我正在构建一个处理钱包的应用程序。我这个钱包有现金入账和现金出账。
当这个钱包的 + 或 - 发生更新时我遇到了问题。
看场景:
用户进行兑现,系统会检索该钱包的余额,如下所示:
select * from Wallet where idUser = x;
所以上面的查询返回钱包,并且*获取“余额”列并这样做
wallet.balance += amount;
此后,我将进行更新以插入该钱包的新“余额”并执行此操作
update Wallet set balance = balanceVar where idWallet = x;
到目前为止,一切看起来都很好,但是当我们处理并发现金进出时,在这种情况下更好的情况是什么。
将其放入队列中?
更新时锁定表?
这是我第一次处理这样的系统,我真的很困惑该怎么办。
您的
SELECT
然后UPDATE
方案直接导致竞争条件,其中两个进程可以选择相同的数据,然后执行相同的更新,从而导致发生两次更新但只记录一次的不一致。
避免这种情况的方法是使用原子操作(即不可分割的操作)。
在您的情况下,您应该直接更新钱包:
update Wallet set balance = balance+10 where idWallet = 'x';
此更新将在另一个进程执行另一次更新之前完全完成。
你可以进一步推进这个想法。对于提款,只有当余额大于或等于提款时,您才能更新钱包:
update Wallet set balance = balance-10 where idWallet = 'x' and balance >= 10;
您可以测试
ROW_COUNT()
,看看操作是否成功。 PHP 为此提供了 mysqli_affected_rows()
或 PDOStatement::rowCount()
。
在这些情况下最好的办法就是使用交易来保证信息的完整性,这样管理者就可以处理在操作信息时可能出现的冲突
TLDR; 您可以使用排队机制来确保更新操作的一致性。
简短示例: 使用 Redis 队列:
出版商:
// Increase or decrease wallet balance, or maybe multiple operations
$redis->lpush('my-queue', json_encode([1234 => '+1']); // ex. 1234 is wallet id
$redis->lpush('my-queue', '-1');
// ...etc
订阅者:
while (true) {
$queueData = $redis->rpop('my-queue');
if (!empty($data)) {
$data = json_decode($queueData);
// process increment or decrement wallet balance based on consumed data
}
}
这将根据排队数据相应更新钱包余额。您可以使用任何排队工具(例如 Redis、RabbitMQ 等)来实现此目的。