SQL Server2000の排他ロックについて

このQ&Aのポイント
  • SQL Server2000を使用している場合、あるトランザクションで排他ロックをかけたレコードが他のトランザクションから共有ロックを使用し参照できてしまう状態が存在するのか疑問です。
  • 具体的な状況として、二つのトランザクションが存在し、それぞれが異なるユーザでコネクションを張っています。autoCommitはfalseに設定され、テーブルにはプライマリキーやインデックスがなく、レコードは10件です。分離レベルはREAD COMMITTEDです。
  • トランザクションAから「SELECT * FROM TEST_TABLE WITH(XLOCK)」を発行し、その後トランザクションBから「SELECT * FROM TEST_TABLE」を発行する場合、トランザクションBの検索はタイムアウトすると予想されますが、実際にはトランザクションBも同じデータが取得されてしまいます。この現象の理由や共有ロックが可能となる条件について詳しい方の助言を求めています。
回答を見る
  • ベストアンサー

排他ロックしたレコードが、別トランザクションから参照されてしまい困っています。

SQL Server2000を使用し、 あるトランザクションで排他ロック(XLOCK)をかけたレコードが 他のトランザクションから共有ロックを使用し参照できてしまう状態は存在するのでしょうか? 排他ロックをかけたレコードが 他のトランザクションから参照できてしまい困っています。 現在、下記の環境で開発を行っております。 ・サーバ側 Windows Server2003 SQL Server2000(sp4) ・クライアント側 Windows Xp(sp3) jdk6.0 jdbc 3.0 Type4 以下が具体的な状況となります。 前提として、 ・二つのトランザクション(以下A、Bと表記します)が存在する。 ・AとBは別のユーザでコネクションを張っている。 ・autoCommitはfalseに設定している。 ・テーブルにプライマリキーやインデックスは張っていない。 ・レコードは10件。 ・分離レベルはREAD COMMITED (1)Aから、「SELECT * FROM TEST_TABLE WITH(XLOCK) 」を発行 (2)Bから、「SELECT * FROM TEST_TABLE」を発行 このような状況で、(1)、(2)の順で処理を行った場合に 私の認識では、(2)の検索時にタイムアウト等が発生するという認識です。 しかし、(2)のSQLは正常に終了し、(1)と同じデータが取得されてしまいます。 ◆その他、確認したこと ・(1)の処理直後に処理を停止し、Enterprise Managerでロックが取得されているか確認したところ、トランザクションAがすべてのレコードを排他ロック(X)していた。 ・(2)の処理直後にロックの状態を確認しても、やはりトランザクションAが、排他ロック(X)していた。 ・CSEを使いODBC経由で同様の操作を行った場合も同じ動作がおこる。 ・(1)WITH(XLOCK, TABLOCK)とすると(2)でデータが取得できなくなる。 ・(2)のSQLを「SELECT * FROM TEST_TABLE WHERE COLUMN01 > 0」のように指定するとタイムアウトする(期待通りの動き) ・(2)のSQLを「SELECT COLUMN01 FROM TEST_TABLE WHERE COLUMN01 > 0」のように指定すると、今度は、なぜか取得出来てしまう。 ・(1)と(2)の間にトランザクションAでUPDATEなどを行うと(COMMITはしない)(2)のSQLのWHERE句や取得するカラムに関係なく、(2)のSQLはタイムアウトする(当り前か。。。) いろいろ書きましたが、排他ロックされたレコードに 共有ロックはかけれないという認識なのですが、 そうではないのでしょうか? また、そうではない場合どういった場合に、 共有ロックが可能となるのでしょうか? SQL Serverの排他制御に詳しい方や 同じような現象に陥った方がいましたら、ご教授お願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • jamshid6
  • ベストアンサー率88% (591/669)
回答No.1

私はロックヒントを殆ど使わないのですが、実際主キーのないテーブルで試してみるとそうなります。 プロファイラで見ても、ロックヒントのないSELECTがテーブルに対するインテント共有ロック以外取得していないように見えます (ちなみにREPEATABLE READでは行共有ロックを取得し、SERIALIZABLEではテーブル共有ロックを取得しますし、主キーのあるテーブルではキーレベルの共有ロックを取得しいくのでXLOCKでブロックできます)。 MSDNのどこにもそのような記述はないので、いろいろ探した結果、あるMSDNのブログで1つだけそれらしい記述のあるものを見つけました。 > SQL Server avoids acquiring read committed locks when it knows that no data has changed on a page. http://blogs.msdn.com/craigfr/archive/2007/04/25/read-committed-isolation-level.aspx この理解が正しいとすれば、「ユーザデータベースでは変更のマークのないページには共有ロックはかけない」となり、XLOCKだけではREAD COMMITTEDのSELECTは阻止できませんから、SERIALIZABLEで排他することが必要になります。 (つまりXLOCK,HOLDLOCK、または、TABLOCKXということになります)

sinjanki
質問者

お礼

ご回答ありがとうございます。 やはり、MSDNの記述は見つかりませんよね。 MSDNに明確にかかれているのが理想なのですが。。。 恥ずかしながら、MSDNブログの存在を初めて知りました。 URLのブログエントリー読ませていただきましたが、 まさに、現状で起こっていることを物語っているように思えます。 jamshid6さんの回答にある通り、XLOCK,HOLDLOCK、または、TABLOCKXを検討することになりそうです。

sinjanki
質問者

補足

もう少し裏付けがあると助かりますので、 この現象に関連する記述を見かけた方いましたら、 ご回答お願いいたします。

関連するQ&A

  • SELECT FOR UPDATE で該当レコードがなかった場合

    SELECT FOR UPDATE ですが、該当レコードのみ ロックすると思うんですが、 該当レコードがない場合は、 ロックできないんでしょうか? たとえば、(COLUMN_BBB が PK として) SELECT * FROM TABLE_AAA WHERE TABLE_AAA.COLUMN_BBB = 'BBB' FOR UPDATE で、そもそも SELECT * FROM TABLE_AAA WHERE TABLE_AAA.COLUMN_BBB = 'BBB' となるレコードがない場合でも、 他トランザクションによる 該当レコードの INSERT を排他防御できるのか無理なのか、 教えていただけたらありがたいです。 すみません、時間的余裕があまりないので、 (すぐに回答ほしいです)でアップします。

  • 排他的ロック

    もともとMicrosoft SQLで使用していたアプリケーションを多少改造し、MySQLにしようと思っています。 データ型もコマンドもすべて一致しているし、と思って構築をしていたら一つ見落としていたことがありました。 排他的ロックです。 MySQLで排他的ロックってどうやってやればいいのでしょう? ちなみに今はこのコマンドをどうすればMySQLで動くのか迷い中です。 SET LOCK_TIMEOUT 1000//ロックのタイムアウトを設定 SELECT * FROM # WITH(XLOCK) WHERE $//排他的ロック

    • ベストアンサー
    • MySQL
  • DELETE文とロックについて

    DELETEしようとしているレコードがロックされている場合は、削除せず すぐに処理を戻したいです。イメージ的にNOWAITが最適と思い →DELETE FROM テーブルA WHERE カラムA = 'A' NOWAIT としたいところですが、NOWAITはSELECT文でしか指定できないとのことなのでNGです。 SELECT文で抽出した条件のレコードを削除する場合 →DELETE FROM テーブルA WHERE カラムA =      (SELECT カラムA FROM テーブルA WHERE カラムA = 'A') と出来ます。 又、SELECT文でロック待機時間なしの場合 →SELECT カラムA FROM テーブルA WHERE カラムA = 'A' FOR UPDATE NOWAIT と出来ます。 これらを組み合わせて、ロックされているレコードを削除しようとした場合、 すぐにNGで制御を戻すように、次のように記載してみました。 →DELETE FROM テーブルA WHERE カラムA =    (SELECT カラムA FROM テーブルA WHERE カラムA = 'A' FOR UPDATE NOWAIT) なぜかNGになってしまいます(右カッコがありませんと言われます)。 なぜこの書き方が出来ないのでしょうか? 現在、一度該当のレコードをSELECT文でFOR UPDATE NOWAITをしてから DELETEをしています。 1つレコードを削除したいだけなのに、わざわざSELECT文と DELETE文を発行してしまっています。 こういう場合、他にどのような方法があるのでしょうか? 宜しくお願いします。

  • トランザクションの考え方

    トランザクションの考え方を教えて下さい。 「データの参照や追加・更新・削除といった処理に矛盾がないことを保証する」という記述を見たのですが、下記の場合も良いのでしょうか。 トランザクションの開始 SELECT * FROM Aテーブル WHERE 項目A = '1' --処理-- UPDATE Aテーブル SET 項目B = '2' WHERE 項目A = '1' トランザクションの終了 開始から終了までの間に他のPCによって SELECT対象のデータが増える可能性があります。 この場合UPDATEするときはやはりSELECTの結果でLOOPするべきなのでしょうか。 上記のようにするとSELECT件数とUPDATE件数は異なってしまうのでしょうか。 環境はVB2005+SQL Server です。

  • ある条件の最大値+1を初番するにはロックが必要ですか?

    以下のテーブルでcolumn毎に連番を振る場合、 テーブルロックが必要でしょうか? テーブル test id column 1 a 2 a 1 b 新規データ登録手順 1.トランザクション 2.select max(id)+1 from test where column = b for update 3.insert test into (id,column) values (selectで取得した値,b) 4.トランザクション終了 これで、column毎に登録されているIDの最大値+1で 重複せずにデータのINSERTが保障されるでしょうか?

    • ベストアンサー
    • MySQL
  • select for updateのロック

    オラクルのselect for updateでロックをするタイミングがいつですか? こんなPL/SQLのコードがあったとします。 ---↓↓↓ソースコードここから↓↓↓------------------------- select * from テーブル1 where id = 1 for update; ・・・・・(a) ~ update テーブル1 set kingaku=100 where id = 1 ・・・・・(b) ~ commit; ---↑↑↑ソースコードここまで↑↑↑------------------------- id = 1のレコードがロックされるのは(a)、(b)どちらのタイミングですか? また、このロックは ・他トランザクションから読めるけど更新できない ・他トランザクションからは読むことすらできない のどちらでしょうか? よろしくお願いします。

  • レコードのコピー

    既存のレコードを1項目だけ変更して同一テーブルに登録します。 こちらで以前質問されていた内容を参考に以下のSQLを作ってみましたが、「SQLコマンドが正しく終了されていません」といわれてしまいます。 どうすればうまくいくのかわかりません。 教えてください。よろしくお願いします。 (ちなみに以下のSQLでの「NO」は変更項目です) INSERT INTO table (column1, column2, column3, KOUSIN_DATE) SELECT A.column1, A.column2, NO, TO_DATE(2005/12/01,'YYYY/MM/DD HH24:MI:SS') FROM table AS A WHERE A.column1 = '9999';

  • DBの行ロックの挙動について教えてください。

    行ロックの挙動について教えてください。 (SQLサーバ、トランザクション分離レベル READ COMMITTED) 行ロックをかけるとロックが掛かった行は他端末から更新・削除などが出来なくなると思います。 逆に言うとロックがかかっていないデータは挿入・更新・削除は自由にできると思っていたのですが、 ロック対象データとは関係ないデータの挿入ができません。そういうものなのでしょうか? ■端末1がロックをかけたデータを端末2が更新する場合 端末1:SELECT * FROM AAA WITH(XLOCK,ROWLOCK) WHERE ID=1 端末2:UPDATE AAA SET ABC='123' WHERE ID=1 ←☆☆端末1がロックかけているので待ち状態になる☆☆ ■端末1がロックをかけたデータを端末2が削除する場合 端末1:SELECT * FROM AAA WITH(XLOCK,ROWLOCK) WHERE ID=1 端末2:DELETE AAA WHERE ID = 1 ←☆☆端末1がロックかけているので待ち状態になる☆☆ ■端末1がロックをかけたデータとは関係ないデータを端末2が挿入する場合 端末1:SELECT * FROM AAA WITH(XLOCK,ROWLOCK) WHERE ID=1 端末2:INSERT INTO AAA (ID,ABC) VALUES (2,'456') ←★★何故か待ち状態になる(そういうもの?)★★ ■端末1がロックをかけたデータとは関係ないデータを端末2が更新する場合 端末1:SELECT * FROM AAA WITH(XLOCK,ROWLOCK) WHERE ID=1 端末2:UPDATE AAA SET ABC='123' WHERE ID=2 ←☆☆端末1がロックをかけていないので更新できる☆☆ ■端末1がロックをかけたデータとは関係ないデータを端末2が削除する場合 端末1:SELECT * FROM AAA WITH(XLOCK,ROWLOCK) WHERE ID=1 端末2:DELETE AAA WHERE ID = 2 ←☆☆端末1がロックをかけていないので削除できる☆☆ 上記のSQL文は説明のため簡素化しています。実際にはWHERE句に主キー以外を指定したりしています。 ロックをかけるSQL文で主キーを指定しているか・いないかで動作は変わりますか?

  • Oracle 9iで英字を含まないレコードの検索

    SQL初心者です。 oracle 9iの環境で「あるカラムに英字を含まないレコードを取得する」というのがあります。 私は10gを使用しているのでREGEXP_LIKE(カラム名_NO,'[A-Z]')とするつもりでした。 ところが9iはREGEXP_LIKEをサポートしていないということです。 ではどうすればできるのかというので困っています。 10gのselect文 select * from table_a where regexp_like(item_a,'[A-Z]') これを9i環境ではどのように記述すればよいのでしょうか?

  • DAOレコードセットから更に絞込みしたい

    VBAで質問です。あるレコードセットXをsqlで取得しました。さらにこのレコードセットにSQLを投げて絞り込みたいと思っています。例えばSQLでA,B,C列を取得しました。このA、B、C列があるレコードセットにwhereで絞り込みたいと思ってます。レコードセットXに向けて、select * from X where...というコードは投げれるのでしょうか?