我在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();
答案 0 :(得分:0)
首先尝试在事务中使用可序列化隔离级别是个好主意。这意味着你至少知道一个转换是什么,并且隔离级别是最大的问题之一。
请注意,serializable并不是真正的可串行化。更多关于this previous answer的内容,当你有时间阅读时: - )。
但最重要的部分是你应该考虑因为序列性失败而对你的事务进行自动回滚是正常的事实,而正确的做法是构建你的应用程序,以便事务可能失败并应该重放。
一个简单的解决方案,对于会计事项我喜欢这个简单的解决方案,因为我们可以预测所有事实,没有惊喜,因此,一个解决方案是执行表锁。这不是一个优雅而优雅的解决方案,没有行级锁定,只是简单的大表锁(并且总是以相同的顺序)。之后,您可以作为单个玩家进行操作,然后释放锁定。表的行上没有多用户并发,没有下一行魔法锁失败(参见上一个链接)。这肯定会减慢你的写入操作,但是如果每个人以相同的顺序执行表锁定,你只会遇到锁定超时问题,没有死锁,也没有“不可序列化的自动回滚”。
修改强>
从您的代码示例中我不确定您可以在开始后设置事务隔离级别。您应该激活MySQL上的查询日志并查看完成的内容,然后检查CMS运行的其他事务是否仍处于可序列化级别。