サーバー2台を用意。トランザクションの分離性について調べた。
ダーティリード、反復不能読取、ファントムリードについて。
ダーティリード
1.begin;
select * from tbl;
id | count
----+-------
1 | 5
2 | 8
3.update tbl set count=10 where id=2;
commit;
2.begin;
4.select * from tbl;
id | count
----+-------
1 | 5
2 | 8
commit;
postgresqlではダーティリードが起こらなかった。つまり、commit前の変更結果がS2で見たときに反映されていなかった。これはpostgresql.confのtransaction_isolationがread_committedになっていたからである。
postgres=# show transaction_isolation;
transaction_isolation
-----------------------
read committed
(1 row)
しかし、postgresqlではRead_uncommittedにしてもダーティリードが起きないようになっている。このことからpostgresqlの運用にあたり、commit仕切るまでのtransaction結果を他サーバーに見せることがなくなるので安心である。(更新途中のデータを閲覧してしまうとかがなくなる)
反復不能読取
1.begin;
select * from tbl;
id | count
----+-------
1 | 5
2 | 8
3.update tbl set count=10 where id=2;
4.commit;
2.begin;
5.select * from tbl;
id | count
----+-------
1 | 5
2 | 10
commit;
S1のcommit後に確認すると、S2でset count=10 where id=2;の結果が見えてしまう。
ポイントはS1のcommit後というところ。DBを更新した後だから見えてしまうのも無理ないでしょという感じ。ただ、他サーバーから見ると実行結果が変わったように映ってしまう。更新前の結果を使って、他サーバーで処理させていた場合ミスが生じる可能性もあるのでそういったときはRepeatable readやSerialzableに設定し、分離性を上げることが賢明。
ファントムリード
反復不能読み取りのinsert版。
1.begin;
3.select * from tbl;
id | count
----+-------
1 | 5
2 | 10
5. insert into tbl values(3,100);
6. commit;
2.begin;
4. select * from tbl;
id | count
----+-------
1 | 5
2 | 10
7. select * from tbl;
id | count
----+-------
1 | 5
2 | 10
3 | 100
8. commit;
S1のinsert→commitのあとにS2でtblを見てみるという流れ。
S2からすると同じコマンドを打っているのに行が追加されたように感じてしまう。反復不能読取と同様更新前の結果を使って処理してる場合は注意が必要。対処法はこちらも同様文れレベルをあげることである。
直列化異常
反復不能読み取り(commitが実行されるまで変更を受け付けない)のために起きてしまう。ファントムリードと原理は同じ。
1.begin;
2.select * from tbl;
id | count
----+-------
1 | 5
2 | 10
1 | 12
3 | 100
(4 rows)
3. insert into tbl select 1,sum(count) from tbl where id=2;
4. select * from tbl;
id | count
----+-------
1 | 5
2 | 10
1 | 12
3 | 100
1 | 10
6. commit;
2.begin;
5. select * from tbl;
id | count
----+-------
1 | 5
2 | 10
1 | 12
3 | 100
(4 rows)
6. insert into tbl select 1,sum(count) from tbl where id=1;
7. select * from tbl;
id | count
----+-------
1 | 5
2 | 10
1 | 12
3 | 100
1 | 17
8. commit;
S1 の4番とS2の7番を見れば明らか。結果が反映されず別々でinsertされている。同時編集をすることで多大なビジネス的な問題が起こるのであればtransaction_isolationをSerializableに設定するのが良い。
まとめ
4つのトランザクション設定における影響を説明した。
分離レベルは
Read_uncommited Read_committed Repeatable_read Serializableというように大きくなるとDBのパフォーマンスに影響するので一概に分離レベルがMaxがいいというわけではない。個人的に小規模開発、小規模利用なら分離レベルはMAXでもいいと思うが、大規模になるとロックする必要が出てきたりはすると思う。