5

我目前正在玩交易,无法围绕以下场景进行思考:

假设有用户名“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"); }

所以时间安排如下:

A 读“约翰·史密斯”

B 读“约翰·史密斯”

A 写“John Smith-1”

B 写“约翰·史密斯-2”

根据我的数据库类事务 B 应该无法写入“John Smith-2”,因为数据在读取后发生了变化。所以事务应该被回滚并且事务A应该获胜。我希望用户名是“John Smith-1”,但结果是“John Smith-2”。

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

亲切的问候

尼尔斯

4

1 回答 1

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从 B 将不会锁定,直到 A 完成,如SERIALIZABLE)。

UPDATE: Meanwhile you can check http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html for pessimistic locking.

于 2012-09-27T13:45:15.293 回答