S-JIS[2021-09-18/2023-10-05]

Tsurugiトランザクション分離レベル

Tsurugiのトランザクション分離レベル(isolation)のメモ。


概要

Tsurugiのトランザクション分離レベル(isolation(アイソレーション))はSERIALIZABLE(シリアライザブル)である。

SERIALIZABLEは、並列で実行した複数のトランザクションに対し、順番(直列)に実行したのと同じ結果が得られるという分離レベル。
例えば2つのトランザクションT1とT2を同時に実行したとき、T1→T2あるいはT2→T1の順で実行したのと同じ結果になる。
ただし、単純にこういう順序で処理するとパフォーマンスが全く出ない為、トランザクション理論を駆使して並列で処理するようになっている。


実行したトランザクションがシリアライザブルにならない場合(データ更新が競合して順序実行したのと同じ結果に出来ない場合)、どちらかのトランザクションはシリアライゼーションエラー(リトライ可能なアボート)になってコミットできない(コミットに失敗する)。[2023-10-05]
シリアライゼーションエラーになったら、トランザクションを再実行することによりトランザクションが成功することが期待される。


他RDBMSとの比較

Tsurugi以外のRDBMS、例えばPostgreSQLやOracleのトランザクション分離レベルのデフォルトはREAD COMMITTEDである。
PostgreSQLやOracleでは、トランザクション分離レベルをSERIALIZABLEに変更することも出来る。

ANSI/ISO SQL標準で定められている分離レベルでは、SERIALIZABLEが最も強い分離レベルで最も安全とされているそうだ。
Tsurugiのトランザクション分離レベルはSERIALIZABLEのみであり、それに特化している。

(ただし、OracleのSERIALIZABLEは、ANSI/ISO SQL標準のSERIALIZABLEとは異なるらしい。
PostgreSQLの「表13.1 トランザクション分離レベル」とOracleの「表9-1 分離レベル別による回避可能な読取り現象」を見比べると、PostgreSQLの方には「直列化異常」という項目があるが、Oracleには無い。この違いのことだろうか?)


SERIALIZABLEの挙動

SERIALIZABLEでは、複数のトランザクションを同時(並列)に実行したときに、順番(直列)に実行したのと同じ結果が得られる。
すなわち2つのトランザクションT1とT2を同時に実行したとき、T1→T2あるいはT2→T1の順で実行したのと同じ結果になる。

READ COMMITTEDとSERIALIZABLE(OCC・LTX)の実行例


SERIALIZABLEのプログラミング

READ COMMITTEDとSERIALIZABLE(OCC・LTX)の実行例の通り、READ COMMITTED(PostgreSQLやOracleのデフォルト)とSERIALIZABLE(Tsurugi)では、得られる結果は同じだとしても、 アプリケーションの実装方法が異なってくる。

SERIALIZABLEでは、シリアライゼーションエラー(リトライ可能なアボート)発生時にアプリケーション側でトランザクションを再実行する必要がある。
Javaの場合、Iceaxeにリトライの仕組みが用意されている。→TsurugiTransactionManager


ロックの考え方

例えば、ある同一レコードを複数のトランザクションから更新する場合、
READ COMMITTEDでは、レコードの更新を競合させないようにする為に、select for update文でレコードをロックするのが常套手段だと思う。
しかしTsurugi(SERIALIZABLE)では、レコードを事前にロックするという方法を採らない。

SERIALIZABLEでは、競合する更新が発生した場合は、どちらかのトランザクションがアボートする。
(TsurugiのLTXでは、一番最初に実行されたトランザクションはアボートしないというルールになっている)
アプリケーションは、トランザクションがアボートしたら(アプリケーションにはシリアライゼーションエラーとして通知される)、トランザクションの先頭から処理を全部再実行(リトライ)しなければならない。

READ COMMITTEDではエラーを発生させない為に事前にロックするが(悲観ロック)、
SERIALIZABLEではエラー(アボート)が発生したらリトライするので(楽観ロック)、
考え方が異なる。

READ COMMITTEDでも、「レコードをINSERTして、一意制約違反が発生したら(他トランザクションによってレコードがINSERT済みだったら)UPDATEに切り替える」という処理を行うことがあるが、SERIALIZABLEはそれと似た考え方かもしれない。


つまり、既存のPostgreSQL用のプログラム(READ COMMITTED前提)をTsurugiに転用する場合、
SQLは「TsurugiのSQL」と「PostgreSQLのSQL」は同等(ただし、Tsurugi 1.0.0では未対応のSQL構文は多い)なのでほとんど変更は 不要かもしれないが、
シリアライゼーションエラー発生時のリトライを実装しなければならない


他でコミットされた値の参照

READ COMMITTEDは、処理中に他トランザクションが値を更新してコミットしたら、その値を読むことが出来る(読んでしまう)が、
SERIALIZABLEでは、理屈上は同じ値しか読まれない。

他トランザクションが更新した値を読みたいという前提でプログラムしているなら、注意が必要。
(むしろ、他トランザクションでコミットされた値が紛れ込まないのが保証されるので、一貫性が担保されて便利な気がするけど)


Tsurugiへ戻る / 技術メモへ戻る
メールの送信先:ひしだま