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

Nil*_*ang 5 ruby activerecord transactions ruby-on-rails

我目前正在玩交易,无法绕过以下场景:

鉴于存在用户名为"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"); }
Run Code Online (Sandbox Code Playgroud)

控制台B:

ActiveRecord::Base.transaction { user = User.find_by_username("foo"); sleep 10; user.update_attribute(:full_name, "#{user.full_name}-2"); }
Run Code Online (Sandbox Code Playgroud)

所以时间如下:

读"约翰史密斯"

B读"约翰史密斯"

写道"John Smith-1"

B写道"John Smith-2"

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

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

亲切的问候

尼尔斯

Vic*_*roz 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,这意味着如果SELECT是非锁定 ( User.find_by...) ,它将不会锁定读取记录,并且您可以从事务 A 启动时创建的快照中获取原始结果(即,SELECT在 A 完成之前,B 不会锁定,就像下面的情况一样)SERIALIZABLE)。

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