LoginSignup
8
6

More than 5 years have passed since last update.

RedshiftのトランザクションのSerializable Isolation Violationエラーについて

Last updated at Posted at 2016-01-21

はじめに

初投稿ですw
観測した現象から推測して知見としてまとめてますので、違ってる可能性はありまーす。ご利用は自己責任でお願いします。もっと詳しいこと知ってたら、教えてエロい人

発生原因と対処法

このエラーは以下のルールに違反するので発生する
http://docs.aws.amazon.com/ja_jp/redshift/latest/dg/c_serial_isolation.html

以下のトランザクションは
同時に実行しても直列化可能なので問題ない。

begin transaction;

delete from target 
using stage 
where target.primarykey = stage.primarykey; 

insert into target 
select * from stage;

end transaction;

しかし以下のようにテーブルへのselectなど、
何かしらの処理を行った場合は
ふたつ目のトランザクションで
Serializable isolation violationが発生し、
ロールバックしてしまった。

begin transaction;

select * from hoge limit 1;

delete from target 
using stage 
where target.primarykey = stage.primarykey; 

insert into target 
select * from stage;

end transaction;

恐らく処理を行う際に
データベースのスナップショットが作成され、
ひとつ目のtransactionのcommit後と差異が発生、
redshiftは直列化可能ではないと認識し
ロールバックするものと思われる。

http://docs.aws.amazon.com/ja_jp/redshift/latest/dg/c_serial_isolation.html
から引用

同時トランザクションは互いに認識されません。互いの変更を検出できません。各同時トランザクションにより、トランザクションの始めにデータベースのスナップショットが作成されます。データベーススナップショットは、ほとんどの SELECT ステートメント、COPY、DELETE、INSERT、UPDATE、TRUNCATE などの DML コマンド、次の DDL コマンドの最初の発生時にトランザクション内で作成されます。

直列化可能に見えるが、redshiftちゃん的には
スナップショットから判断してNGらしい。。厳しい。。

なお
select * from hoge limit 1;
ではなく
select 1;
だと
データベースのスナップショットが作成されないようで
エラーは発生しない。

またtempテーブルは同じ名前でも処理ごとに別物なので
selectして更新しようが何しようが直列化可能なのでエラーは発生しない

systemコマンドでsqlを流した場合とActiveRecordでsqlを流した場合のトランザクションの違い

以下のsqlをファイルに書き込んで、systemコマンド(psql ~ < sqlのファイル)で流した場合はトランザクションは全て別のものになる

select * from hoge limit 1;
select * from hoge limit 2;

begin transaction;
select * from hoge limit 3;
end transaction;

select * from hoge limit 4;

begin transaction;
select * from hoge limit 5;
end transaction;
SQL

しかし以下のSQLをActiveRecordで流した場合

sql = <<SQL
select * from hoge limit 1;
select * from hoge limit 2;

begin transaction;
select * from hoge limit 3;
end transaction;

select * from hoge limit 4;

begin transaction;
select * from hoge limit 5;
end transaction;
SQL

ActiveRecord::Base.connection.execute(sql)

トランザクションは以下のようになる

トランザクション1
select * from hoge limit 1;
select * from hoge limit 2;
select * from hoge limit 3;
トランザクション2
select * from hoge limit 4;
select * from hoge limit 5;

ActiveRecordの場合、begin transactionがない場合は勝手にbegin transactionを付加しているような挙動をするということである。(ActiveRecordのどこで行っているのか現在確認中 Adapter?)

ActiveRecordでRedshiftに接続して使う場合は、この辺りを理解していないとSerializable isolation violationのワナにかなりハマりやすくなる

おまけ

最近ロールバックしちゃったトランザクションを確認できるSQL

select stl.*,sv.table
from stl_tr_conflict stl
left join SVV_TABLE_INFO sv ON stl.table_id = sv.table_id
order by abort_time desc limit 100;

特定のクエリのトランザクションID(xid)を確認

select starttime,xid, querytxt from stl_query
where querytxt LIKE '%select * from hoge limit 4%' 
order by starttime desc  limit 1;

特定のトランザクションのクエリを確認

select starttime,querytxt from stl_query
where xid = 5864985 order by starttime;
8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6