[英]Why does this transaction not work in ActiveRecord?
我目前正在玩交易,无法绕过以下场景:
鉴于存在用户名为“johnny”且全名为“John Smith”的用户。
我启动两个rails控制台并按此顺序执行以下命令:
控制台A:
ActiveRecord::Base.transaction { user = User.find_by_username("foo"); sleep 10; user.update_attribute(:full_name, "#{user.full_name}-1"); }
控制台B:
ActiveRecord::Base.transaction { user = User.find_by_username("foo"); sleep 10; user.update_attribute(:full_name, "#{user.full_name}-2"); }
所以时间如下:
读“约翰史密斯”
B读“约翰史密斯”
写道“John Smith-1”
B写道“John Smith-2”
根据我的数据库类事务,B应该无法编写“John Smith-2”,因为数据自读取后就已更改。 因此,交易应该回滚,交易A应该赢。 我希望用户名是“John Smith-1”,但结果是“John Smith-2”。
任何想法为什么会发生这种情况或如何获得预期的行为?
亲切的问候
尼尔斯
据我所知,事务不是关于锁定,事务的主要目的是确保原子的变化。 例如,当您从支票账户中扣除资金并将其存入储蓄账户时,您需要确保INSERT成功或两者都失败,否则您将处于不一致状态。 您需要的是锁定,例如http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html 。
更新:根据我的理解,ACID并不意味着回滚事务。 如果没有错误,则两个事务都将成功。 你得到的结果取决于隔离。 如果使用SERIALIZABLE
级别,您将获得“John Smith-1-2”,但InnoDB默认使用REPEATABLE READ
级别http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html #isilevel_repeatable-read ,这意味着如果SELECT
是非锁定的( User.find_by...
),它将不会锁定记录以进行读取,并且您从事务A启动时创建的快照中获得原始结果(即从B中SELECT
直到A完成后才会锁定,如同SERIALIZABLE
)。
更新:同时您可以查看http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html以获得悲观锁定。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.