【问题标题】:Updating account balances with mysql使用 mysql 更新账户余额
【发布时间】:2012-01-11 23:32:25
【问题描述】:

我在 User 表中有一个字段,用于保存用户的帐户余额。用户可以使用我的服务执行很多操作,这将导致他们的余额快速变化。

我正在尝试使用 mysql 的可序列化隔离级别来确保多个用户操作不会错误地更新值。 (动作 A 和动作 B 同时想从余额中扣除 1 美元。)但是,我遇到了很多死锁错误。

我如何正确地做到这一点而不会出现所有这些死锁,并且仍然保持余额字段是最新的?

简单模式:用户有一个 id 和一个余额。

我正在使用学说,所以我正在做类似以下的事情:

$con->beginTransaction();
$tx = $con->transaction;
$tx->setIsolation('SERIALIZABLE');

$user = UserTable::getInstance()->find($userId);
$user->setBalance($user->getBalance() + $change);
$user->save();
$con->commit();

【问题讨论】:

  • 如果您提供了架构以及选择、更新以及所有事务性完成的访问模式,那么解决这个问题会更容易。

标签: mysql serialization


【解决方案1】:

首先尝试在您的事务上使用可序列化的隔离级别是个好主意。这意味着您至少知道什么是转换,并且隔离级别是最大的问题之一。

请注意,可序列化并不是真正的可序列化。更多关于 this previous answer 的内容,您将有时间阅读:-)。

但最重要的部分是您应该考虑到由于串行性失败而对事务进行自动回滚是正常的事实,正确的做法是构建您的应用程序,以便事务可能失败并且应该重播。

一个简单的解决方案,我喜欢这个简单的解决方案,因为我们可以预测所有事实,没有意外,因此,一个解决方案是执行表锁定。这不是一个精致而优雅的解决方案,没有行级锁,只是简单的大表锁(并且总是以相同的顺序)。之后,您可以作为单人玩家进行操作,然后释放锁。表的行上没有多用户并发,没有下一行魔法锁失败(参见上一个链接)。这肯定会减慢您的写入操作,但如果每个人都以相同的顺序执行表锁,您只会遇到锁超时问题,不会出现死锁,也不会出现“不可序列化的自动回滚”。

编辑

从您的代码示例中,我不确定您是否可以在 开始之后设置事务隔离级别。您应该在 MySQL 上激活查询日志并查看完成了什么,然后检查 CMS 运行的其他事务是否仍处于可序列化级别。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-23
    • 1970-01-01
    • 2015-08-31
    • 2018-01-12
    • 1970-01-01
    • 2011-12-09
    • 2021-07-22
    • 2020-08-15
    相关资源
    最近更新 更多