[pgsql-jp: 30191] Re: Serial型の列とトランザクション

Jun Kitamura kitamura @ zoozee.jp
2003年 6月 18日 (水) 11:18:17 JST


北村です。

> > insert into tb ( id,int,txt )
> > values (
> >  (select max(t2.id) + 1 from tb t2),
> >   11,
> >   'abc'
> > );
> > というようにテーブルロックを使わずに書いてあるのを見たことが
> > ありますが、どれくらいの確率か解りませんが、タイミングによっ
> > ては id が競合してしまう(PRIMARY KEY ならエラー)となる「気が
> > します」。
> 
> 上記のinsert文を複数のトランザクションで実行しても,
> 期待した動作をするのではないかと思います.
> 
> なぜなら,select max(...) にて,表tb全体を(t2という相関名で)
> 参照していますから,insert文を実行する間は,max(t2.id)の
> 値は変わらないと考えられるからです.
> 
> #あるいは,隔離性水準を serializable としてファントム防止が
> #なされていれば,insert文の実行が完了するまで,このテーブルの
> #中身は他のトランザクションからは見えないと思います.

説明不足で申し訳ないのですが、上記 SQL のみが多セッションか
ら実行されるのです。AutoCommit で暗黙的トランザクションを利
用していました。BEGIN があり、その次にテーブルロック、そして
上記 SQL、COMMIT であれば問題ないと思います。
単文で実行される上記 SQL は、
1)テーブルtb 、カラムid の最大値を SELECT
2)その値に1をプラス
3)テーブルtb にインサート
という流れにすぎません。このトランザクション(T1)が終了する前
に他のトランザクション(T2)が同じ SQL を実行した場合、1) で 
SELECT される値は T1 の値と同値になってしまいます。

create table seqtest(
 a int4,
 created timestamp default now ()
);
で作成されるテーブルに、
insert into seqtest(a) values (
 (select max(t1.a) + 1 from seqtest t1)
);
という単文を 2台の端末からほぼ同時に「1000回ループ」を実行し
ます。(この前に insert into seqtest(a) values (1) を実行して、
max()関数の戻り値に NULL が出ないようにしました)。
2000レコード作られるわけですが、テーブルに格納される順番は無
視して、カラムa の「欠番がない」「同値が存在しない」事を期待
していました。
結果は先にも話したとおり、期待通りだった・・・わけです。汗
期待通りだったのですが、理屈から言うと「同値が存在してしまう」
ハズなのに、ならない。実際、上のやり方で5回ほどやったのです
が、「欠番なし」「同値なし」で出来てしまいました。
CPUパワーやメモリ、HDDといった要素で「たまたま」上手くいった
という結論にしました。

> (commitであれrollbackであれ)トランザクションの終了後は,
> キャッシュされていた(=予約されていた)値は全て破棄されます.

おぉ。ありがとうございます。
ちょっと気になっていたので(笑。





pgsql-jp メーリングリストの案内