• 締切済み

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文で主キーを指定しているか・いないかで動作は変わりますか?

みんなの回答

  • asciiz
  • ベストアンサー率70% (6634/9399)
回答No.2

>テーブル ヒント (Transact-SQL) - SQL Server | Microsoft Learn >https://learn.microsoft.com/ja-jp/sql/t-sql/queries/hints-transact-sql-table こちらのページの注釈に、次の記述がありました。 >行レベルのロックを獲得するロック ヒント ROWLOCK、UPDLOCK、XLOCK では、 >実際のデータ行ではなくインデックス キーに対してロックが実行される場合があります。 SELECT の書き方により、インデックスがロックされる場合があるとのことで、インデックスがロックされたなら、UPDATE はできても INSERT 出来ないことになるのかもしれません。 まあつまり結局回答No.1さんと同じことを言っている気がします(汗

  • t_ohta
  • ベストアンサー率38% (5076/13258)
回答No.1

INSERTだからかもしれませんね。 ユニークインデックスが絡むと、ロックされている行の処理とダブる可能性を考えて排他制御の対象になっているのではないでしょうか。

関連するQ&A

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

    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の排他制御に詳しい方や 同じような現象に陥った方がいましたら、ご教授お願いします。

  • 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)どちらのタイミングですか? また、このロックは ・他トランザクションから読めるけど更新できない ・他トランザクションからは読むことすらできない のどちらでしょうか? よろしくお願いします。

  • 別テーブルからSELECTした値を持つ行を削除するSQLは?

    削除 SQL がわかりません。 SQL-1 SELECT id FROM table2 WHERE date_in IS NULL; これで取得したidを持つレコード(別テーブル)を削除したいのですが、 どうすれば組み合わさるのでしょうか? DELETE FROM table1 WHERE id=???

  • 行ロックについて

    Web上のボタンをクリックしたとき 該当データを1件取得しそのデータにフラグをたてる ⇒フラグを管理するテーブルに投入する という処理をしており該当データは一人につき1件取得したいです。 BEGIN SELECT * FROM TABLE1 WHERE day = 今日 ORDER BY time LIMIT 1 FOR UPDATE UPDATE TABLE1 SET flag = 1 WHERE id = $id INSERT INTO TABLE2 (time , flag ) VALUES ($time , 1); COMMIT 現在の問題は同時にボタンを押したとき SELECTで取得したデータが重複してしまうことがあることです。 BEGINからCOMMITまでを一連の流れとし、 その間はSELECTもできないようにしたいです。 ドキュメントに FOR UPDATEだとUPDATE、DELETE、SELECT FOR UPDATEは拒否されます とありますのでSELECTは通ると解釈するとFOR UPDATEでは実現できないのでしょうか? テーブルのロックではなく行のロックで実現したいのですが どなたかご教授お願い致します。 ※バージョンはPostgreSQL 8.1.18 です。

  • 一定以上の行があれば最大値を持つ行を削除するSQL

    MySQL5にて、あるテーブルの特定フィールドの数(つまりcount値)が一定値以上であれば、間引きのため別のフィールドの値で最大値をもつ行を削除するSQLを考案中です。 イメージとしては以下の通りですが、SQLとしては誤っており、また、冗長です。 DELETE FROM table WHERE id={$id} AND (SELECT COUNT(*) FROM table WHERE id={$id}) > 5 AND begin = (SELECT MAX(begin) FROM table WHERE id={$id}); よい知恵をお授けください。

    • ベストアンサー
    • MySQL
  • DB2のSQL

    select * from xTABLE where EMP_ID = "P001" というSQLを実行すると SQL0206N "P001" is not valid in the context where it is used. SQLSTATE=42703 のエラーが出ます。 SQLSTATEで調べても「"P001"の列がテーブルにない」ということみたいですが 列名で捉えている時点でおかしい気がします select * from xTABLE where EMP_ID = null のSQLは正常にテーブルのSELECT結果が返ってきます。 どうすればよいでしょうか?

  • テーブル上に存在しないデータの一覧を取りたい。

    mysql+phpで開発を行っています。 テーブル id 101 102 104 106 109 110 の様にデータが入っているテーブルaaaがあります。 プログラムで取得したid一覧が上記テーブルにレコードがあるのか確認するSQLは 例)select id from aaa where id in (101,102) で取得できるということは分かっています。 ここで select id from aaa where id in (102,103) とやると102の1件が取得できます。 今回知りたいことは 逆に取得できなかった103という値を取得する方法はないのかという事です。 select id from aaa where id=102; select id from aaa where id=103; 2回SQLを実行して値が戻ってこないSQL=テーブルにそのレコードが無いというプログラムを書けばいい事は分かっています。 しかし、処理が少なくとも数千回発生してしまう予定ですのでできれば別の方法がいいと思っています。 また、in区で使っている一覧はテーブルには入っておらず、また、こちらも数千個あるので、一時テーブルに入れるといってもかなりの時間がかかってしまうと思います。 何か一発で取得できるいい方法などありますでしょうか? ちなみにaaaテーブルは5万件ほどなので全件php側でメモリに展開してサーチも避けたいです よろしくお願いいたします。

  • 文字列のフィールドのOrderbyについて

    Oracle9iです。(厳密にはVB6も絡んでいます) 説明の便宜上、若干簡略化しています。 テーブルtestがあります。 SQL> desc test; 名前 NULL? 型 --------- -------- ---------------- ID VARCHAR2(10) です。 (1)testテーブルのデータが、 SQL> select id from test ; ID ---------- 3 5 11 551 のとき、select id from test where id between 1 and 4;を実行すると、 ID ------- 3 select id from test where id between '1' and '4';を実行すると、 ID ------- 3 11 となってしまいます。 別のケースで、 SQL> select id from test ; ID ---------- 3 5 11 551 C44433 aaa 6行が選択されました。 のデータの場合、 select id from test where id between 1 and 4;はORA-01722:数字が無効です のエラーとなり、 select id from test where id between '1' and '4';だと ID ------- 3 11 となってしまいます。 自分の希望としては select id from test where id between '1' and '4'にて、 ID ------- 3 のみ表示をさせたいです。 (2)SQL> select id from test ; ID ---------- 3 5 11 551 C44433 aaa のとき、たとえば、 select id from test where id between '12' and 'DD'; を実行し、12以上でDD以下のデータを取得したいのですが、 (理想) ID ---------- 551 C44433 実際は、 ID ---------- 3 5 551 C44433 と出てしまいます。 仕様は上司が考えてるので、 テーブル構造にそもそも問題があるのならそれ相応の説明をしたいと思っています。 ですが、基本的にはSQLの条件文を変更したいのですが、 アドバイスをいただけたらと思います。 ちなみに説明の簡略化のためフィールドを少なくしていますので select RPAD(id , 10) from test where ~ のように select のあとに変換するのはできません。 実環境は基本的には select * from ~としたいので。 よろしくお願いいたします。

  • SELECT時の行ロックの必要性について

    SELECT ~ FOR UPDATEやSELECT ~ LOCK IN SHARE MODEという行ロックがあり、この件について解説しているサイトをいろいろ見ているのですが、振る舞いが複雑だということはわかりました。 しかしそもそもなぜSELECTで行ロックするのか、運用上でどんなときに使うのかを解説しているサイトは見当たりませんでした。 私は、SELECTするときは単に SELECT * FROM tb_a WHERE id = 1; としか記述していません。 質問1. なぜSELECTするのにトランザクションが必要なのでしょうか?運用上でどんなときにSELECTでトランザクションを使うのでしょうか?よく解説サイトには、 BEGIN; SELECT * FROM tb_a WHERE id = 1 FOR UPDATE; COMMIT; と書いています。SELECTするのに行をロックする必要性がいまいちわからないです。 質問2. トランザクションを開始したときにロックがかかるのではないのでしょうか?つまりBEGIN;でロックがかかるわけではないのですか?SELECTのクエリーにFOR UPDATEと書くということはこのSELECTのコードが実行された時点でロックがかかるのでしょうか? 質問3. FOR UPDATEやLOCK IN SHARE MODEというのはSELECTにしか使えないのか、もしくはSELECTだから意味があるのでしょうか?UPDATEやDELETEは単にBEGINE;とCOMMIT;で囲えばいいだけですよね? 質問4. 以下のようなコードを解説しているサイトがあります。 BEGIN; SELECT * FROM tb_a WHERE id = 1; COMMIT; SELECT文にはFOR UPDATEも書いていないのですが、これは何を意味するのでしょうか?つまり、SELECTするのになぜトランザクションを実行するのでしょうか? 一番知りたいのは運用上どのような場合にSELECTでトランザクションを使って行ロックするのか、ということです。 どうぞよろしくお願い致します。

    • ベストアンサー
    • MySQL
  • 排他的ロック

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

    • ベストアンサー
    • MySQL