为什么此事务在ActiveRecord中不起作用?

时间:2012-09-27 12:19:29

标签: ruby-on-rails ruby activerecord transactions

我目前正在玩交易,无法解决以下问题:

鉴于用户名为“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”

A写道“John Smith-1”

B写道“John Smith-2”

根据我的数据库类事务,B应该无法写入“John Smith-2”,因为数据自读取后就已更改。因此,交易应该回滚,交易A应该赢。我希望用户名是“John Smith-1”,但结果是“John Smith-2”。

为什么会发生这种情况或如何获得预期行为?

亲切的问候

尼尔斯

1 个答案:

答案 0 :(得分:4)

据我所知,事务不是关于锁定,事务的主要目的是确保原子的变化。例如,当您从支票账户中扣除资金并将其存入储蓄账户时,您需要确保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#isolevel_repeatable-read,这意味着如果SELECTUser.find_by...非锁定(SELECT)它不会锁定记录以进行读取,并且您从事务A开始时创建的快照获得原始结果(即来自B的SERIALIZABLE将不会锁定,直到A完成为如果是{{1}})。

更新:同时您可以检查http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html是否存在悲观锁定。