[pgsql-jp: 29155] Re: 同時更新の制御2

Takeshi Miyakawa tak @ hdt.co.jp
2003年 2月 20日 (木) 11:44:24 JST


 みやかわ@ホビー・データです。

 下記の課題を前提にします。

>表に以下の値が入っているとします。
>  a  b
>------
>  1  1
>  2  1
>
>この表に対してCの埋込SQLにて以下を行います。
>(ここで、wk_a,wk_bはホスト変数とします)
>EXEC SQL DECLARE test_cur CURSOR FOR SELECT * FROM 表;
>EXEC SQL OPEN test_cur;
>EXEC SQL FETCH IN CURSOR INTO :wk_a,:wk_b;
>EXEC SQL UPDATE 表 set b=:wk_b+1 where a=:wk_a;・・・(1)
>EXEC SQL FETCH IN CURSOR INTO :wk_a,:wk_b;
>EXEC SQL UPDATE 表 set b=:wk_b+1 where a=:wk_a;・・・(1)
>EXEC SQL COMMIT;
>
>注:(1)のところで、bに設定する値はホスト変数を使用します。
>このプログラムを2つ同時に走行した場合、(a,b)=(1,2),(2,2)となります。
>(a,b)=(1,3),(2,3)を得るためにはどのようにすれば良いでしょうか?


 まず、ORDER BYを指定していないSELECTで行の選択される順序は不定です。
 ですから最初のFETCHで得られる行は、(a,b)=(1,1)かも知れませんし、
(a,b)=(2,1)かも知れません。

 (a,b)=(1,1)だったと仮定します。すると直後のUPDATEは
SET b=1+1 WHERE a=1
 となります。この結果、表の状態は以下の通りになります。
  a  b
------
  1  2
  2  1
 次のFETCHによって(a,b)=(2,1)である行が選択されます。すると直後の
UPDATEは
SET b=1+1 WHERE a=2
 となります。この結果、表の状態は以下のとおりとなります。
  a  b
------
  1  2
  2  2

 次に(a,b)=(2,1)が先に選択されたと仮定します。すると直後のUPDATEは
SET b=1+1 WHERE a=2
 となります。この結果、表の状態は以下の通りになります。
  a  b
------
  1  1
  2  2
 次のFETCHによって(a,b)=(1,1)である行が選択されます。すると直後の
UPDATEは
SET b=1+1 WHERE a=1
 となります。この結果、表の状態は以下のとおりとなります。
  a  b
------
  1  2
  2  2

 以上が1つのプロセスが実行する詳細です。
 2つのプロセスが実行する場合、何が起きるでしょうか?
 答えは、カーソルをFETCHして得られる値は、カーソルを宣言したときの
値です。したがって2つのプロセスが同時にこれらの作業を実行した場合、
UPDATE前にカーソルが宣言されているので、どちらのプロセスにおいても、
UPDATE前の値がFETCHされることになります……つまり、結果は上記とまっ
たく同一です。
 これを回避するためには、トランザクションを開始し、FETCHの前に表
をロックしておくことです。そうすれば、片方のプロセスはロックが解除
されるまでカーソルを作れませんから、期待通りの挙動となるはずです。

BEGIN WORK;
LOCK TABLE 表;

 を処理の最初に付加すれば良いはずです(試してませんが)。

-----

 しかし、意味がよくわからんのですよね。これは単にb列の値を1加算し
たい、というだけですか? だったら、カーソルなど使わずに、

UPDATE 表 SET b=b+1;

 で良いと思うのですが。これならトランザクションも不要です。

At 午前 10:52 03/02/19 +0900, TVH51179 @ biglobe.ne.jp wrote:
>小林です。いつもお世話になっております。
>
> >  みやかわ@ホビー・データです。
> >
> >  ??? そのためにトランザクションがあるのでは?
> >
>ご指摘の通りでございます。
>
>トランザクションは片方が更新していても読込はでき、
>更新時に片方のcommitを待つと認識しております。
>
>でも、今回は
>片方が更新しようとすると、もう片方で読み込んでいるのでWAITし、
>                  ^^^^^^^
>もう片方においても更新時に、片方が読み込んでいるのでWAITして、
>             ^^^^^^^
>”デッドロックを検出する”みたいなことが可能なのかな?
>と思ったのですが、どうなんでしょう??
>
>と、いうのは、カーソルを使用して順次読み込みを行っているので、
>カーソルの定義時に"for update"が使えなくて困っている次第であります。
>FETCH後にselectするのも格好悪いし・・・
>
>何かいい方法ないでしょうか?
>
>文章能力低くて申し訳ないです・・・よろしくお願いいたします。
>
>小林 克彦(TVH51179 @ biglobe.ne.jp>
> > At 午後 05:59 03/02/18 +0900, TVH51179 @ biglobe.ne.jp wrote:
> > >小林と申します。いつもお世話になっております。
> > >
> > >度々申し訳ありません。
> > >
> > >RedHatLinux-7.2にて
> > >Postgres-7.3.1を使用しております。
> > >
> > >2つのプロセスが同じレコードに対して同時に読み込み&更新
> > >(読み込み−読み込み−更新−更新)を行おうとしています。
> > >
> > >片方のプロセスが更新時にもう片方のプロセスが読み込んでいるのを検出して
> > >WAITすることは可能なのでしょうか?
> > >
> > >または、
> > >
> > >片方のプロセスにて更新が終わり、COMMITした場合、もう片方のプロセスにて
> > >更新時にデータが更新されているのを検出することは可能なのでしょうか?
> > >
> > >よろしくお願い致します。
> > >
> > >−−−−−−−−−−−−−−−−−−−
> > >小林 克彦(TVH51179 @ biglobe.ne.jp> >
> > ---
> > Takeshi Miyakawa <tak @ hdt.co.jp>
> > http://www2.hdt.co.jp/~tak/index.html
> >
> >


---
Takeshi Miyakawa <tak @ hdt.co.jp>
http://www2.hdt.co.jp/~tak/index.html




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