- 公開日
Rails でトランザクション分離レベルを設定する方法
目次
追記
Rails5, Rails6 向けに記事の内容をアップデートしました(2019/09/21)
MySQLのトランザクション分離レベル
突然ですが問題です。MySQLのデフォルトのトランザクション分離レベルは何でしょうか?
REPEATABLE READ
This is the default isolation level for InnoDB.
via. MySQL :: MySQL 8.0 Reference Manual :: 15.7.2.1 Transaction Isolation Levels
ハイ、答えは「REPEATABLE READ」ですネ。
Railsでトランザクション分離レベルを設定
続いての問題です。Railsにおいてトランザクション分離レベルを設定するにはどうしたらよいでしょうか? 実は Rails 3 と Rails 4 と Rails 5 以降ではトランザクション分離レベルの設定方法はそれぞれ異なっています。
Rails 3
Rails 3の時代では execute
で直接トランザクション分離レベルを設定する必要がありました。
ActiveRecord::Base.connection.execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED')
ActiveRecord::Base.transaction do
# ...
end
Rails 4.x
Rails 4からはtransaction
のオプションとしてトランザクション分離レベルを設定可能になりました。
ActiveRecord::Base.transaction(isolation: :read_committed) do
# ...
end
Rails 5 以降
Rails 5からは ActiveRecord::Base
の代わりに ApplicationRecord
が使うことができます。
ApplicationRecord is a new superclass for all app models
via. Ruby on Rails 5.0 Release Notes — Ruby on Rails Guides
ApplicationRecord.transaction(isolation: :read_committed) do
user = User.lock.find(1)
user.update! name: "TEST"
end
上記のコードを pry で実行した際に流れるクエリは下記の通りです。
(0.5ms) SET TRANSACTION ISOLATION LEVEL READ COMMITTED
(0.3ms) BEGIN
User Load (0.9ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1 FOR UPDATE
User Update (0.4ms) UPDATE `users` SET `name` = 'TEST', `updated_at` = '2019-09-20 16:34:31' WHERE `users`.`id` = 1
(1.5ms) COMMIT
=> true
有効なトランザクション分離レベル
Railsで設定可能かつ有効なisolation
レベルは何でしょうか? 答えは下記4つになります。
Valid isolation levels are:
:read_uncommitted
:read_committed
:repeatable_read
:serializable
via. ActiveRecord::ConnectionAdapters::DatabaseStatements | RailsDoc
分離レベルとダーティリード、ファジーリード、ファントムリードの関係
分離レベルとダーティリード、ファジーリード、ファントムリードそれぞれの関係性は以下の通り。
トランザクション分離レベル | ダーティリード | ファジーリード | ファントムリード |
---|---|---|---|
READ UNCOMMITTED | 💀発生する | 💀発生する | 💀発生する |
READ COMMITTED | 発生しない | 💀発生する | 💀発生する |
REPEATABLE READ | 発生しない | 発生しない | 💀発生する |
SERIALIZABLE | 発生しない | 発生しない | 発生しない |
via. [RDBMS][SQL]トランザクション分離レベルについて極力分かりやすく解説 - Qiita
最後に
適切なトランザクション分離レベルで適切なトランザクション処理をしましょう!