質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Q&A

解決済

2回答

7412閲覧

PostgreSQLにおけるinsertとdeleteの競合について

退会済みユーザー

退会済みユーザー

総合スコア0

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

1グッド

4クリップ

投稿2019/08/01 08:59

編集2019/08/01 11:18

PostgreSQL 9.6 にて、以下の事象が理解できずに困っております。

二つのトランザクションA,Bがあり、Aは既存レコードのdeleteを、Bは既存レコードのPKの値と同じ値をもつレコードのinsertを行います。
TX Aから開始し、delete直後にTX Bを開始します。

デフォルトのテーブルの状態は以下です。

psql

1> select * from hoge; 2id 3---- 4 1 5(1 row)

psql

1TX A: 2> begin; 3BEGIN 4> delete from hoge where id = 1; 5DELETE 1 6# ここでTX Bを開始 7# TX Bのinsert実行後に再開 8> commit; 9COMMIT

psql

1TX B: 2> begin; 3BEGIN 4> insert into hoge values (1); 5# TX Aがcommitされるまでinsertが実行されない ※1 6INSERT 0 1 7> commit; 8COMMIT

TX Bの※1部分でinsert処理が待たされますが、この挙動は以下のように考えました。
リファレンス を確認しますと、deleteとinsertではテーブルレベルロック ROW EXCLUSIVE を取得している認識です。

ROW EXCLUSIVE
SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE、およびACCESS EXCLUSIVEロックモードと競合します。

UPDATE、DELETE、およびINSERTコマンドは、(参照される他の全てのテーブルに対するACCESS SHAREロックに加えて)対象となるテーブル上にこのモードのロックを獲得します。 通常、このロックモードは、テーブルのデータを変更する問い合わせにより獲得されます。

しかし、ロックモードの競合 を確認しますと、 ROW EXCLUSIVE 同士は競合となり得ないように見受けられました。

上記の挙動の理由/仕組みをご教授頂けないでしょうか。
宜しくお願いいたします。

urbainleverrier👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yambejp

2019/08/01 11:15

Postgreで確定しているならMySQLタグは外しておいたほうがよいでしょう
KOZ6.0

2019/08/02 06:49

hoge テーブルにプライマリキーがあって行ロックがかかっているのでは? TX B を insert into hoge values (2); にしたらどうなりますか?
退会済みユーザー

退会済みユーザー

2019/08/03 02:03

ご指摘ありがとうございます。insertに成功しました。
guest

回答2

0

ベストアンサー

PostgreSQLではSQL標準規格のトランザクション分離レベルのREAD COMMITTEDをデフォルトに設定しています。
PostgreSQLのREAD COMMITTEDにおける insert の仕様は、今回の再現手順を実行するとShareLockを取得しようとして待たされるため、PostgreSQLのREAD COMMITTEDにおける UPDATEDELETESELECT FOR UPDATE 、および SELECT FOR SHARE と同じ仕様ではないかと考えられます。

まとめますと

  • delete および insert は ROW EXCLUSIVE テーブルレベルロックを取得する(競合しない)。
  • PostgreSQLのREAD COMMITTED(デフォルト)における insert の仕様は、他トランザクションによってその結果が可視となっていない( commit / rollback されていない)場合、そのトランザクションがcommit / rollback されるのを待つ。

  • 現在のトランザクション分離レベルの確認
show transaction isolation level; transaction_isolation ----------------------- read committed (1 row)

read committed分離レベル

PostgreSQLではリードコミッティドがデフォルトの分離レベルです。 トランザクションがこの分離レベルを使用すると、SELECT問い合わせ(FOR UPDATE/SHARE句を伴わない)はその問い合わせが実行される直前までにコミットされたデータのみを参照し、まだコミットされていないデータや、その問い合わせの実行中に別の同時実行トランザクションがコミットした更新は参照しません。 結果として、SELECT問い合わせはその問い合わせが実行を開始した時点のデータベースのスナップショットを参照することになります。 しかしSELECT文は、自分自身のトランザクション内で実行され更新された結果はたとえまだコミットされていなくても参照します。 単一のトランザクション内であっても、SELECT文を2回連続して発行した場合、最初のSELECT文が開始した後で2番目のSELECT文が開始する前に他のトランザクションが更新をコミットすると、最初とその次に発行したSELECT問い合わせは異なるデータを参照してしまうことにも注意してください。

UPDATE、DELETE、SELECT FOR UPDATE、およびSELECT FOR SHAREコマンドは対象行を検索する際にSELECTコマンドと同じように振舞います。 これらのコマンドは、問い合わせが開始された時点で既にコミットされた対象行のみを検出します。しかし、その対象行は、検出されるまでに、同時実行中の他のトランザクションによって、既に更新(もしくは削除あるいはロック)されてしまっているかもしれません。 このような場合更新されるべき処理は、最初の更新トランザクションが(それがまだ進行中の場合)コミットもしくはロールバックするのを待ちます。 最初の更新処理がロールバックされるとその結果は無視されて、2番目の更新処理で元々検出した行の更新を続行することができます。 最初の更新処理がコミットされると、2番目の更新処理では、最初の更新処理により行が削除された場合はその行を無視します。 行が削除されなかった時の更新処理は、最初のコミットで更新された行に適用されます。 コマンドの検索条件(WHERE句)は、更新された行がまだその検索条件に一致するかどうかの確認のため再評価されます。 検索条件と一致している場合、2番目の更新処理は、更新された行を使用して処理を開始します。 SELECT FOR UPDATEおよびSELECT FOR SHAREの場合、ロックされクライアントに返されるのは、更新されるバージョンの行であることを意味します。

pg_locks

指定されたプロセスにより保持されているロックを表す行内ではgrantedは真です。偽の場合はこのロックを獲得するため現在プロセスが待機中であることを示しています。つまり、同じロック対象のオブジェクトに対して何らかの他のプロセスが競合するロックを保持、もしくは待機していることを意味します。 待機中のプロセスはその別のプロセスがロックを解放するまで活動を控えます。 (もしくはデッドロック状態が検出されることになります)。 単一プロセスでは一度に多くても1つのロックを獲得するために待機します。

pg_locks(原文)

Throughout running a transaction, a server process holds an exclusive lock on the transaction's virtual transaction ID. If a permanent ID is assigned to the transaction (which normally happens only if the transaction changes the state of the database), it also holds an exclusive lock on the transaction's permanent transaction ID until it ends. When a process finds it necessary to wait specifically for another transaction to end, it does so by attempting to acquire share lock on the other transaction's ID (either virtual or permanent ID depending on the situation). That will succeed only when the other transaction terminates and releases its locks.

  • TX Aにてdelete実行直後のロック状態

psql

1> SELECT l.pid,d.datname,l.locktype,relation::regclass,transactionid,l.mode,l.granted 2 FROM pg_locks l LEFT JOIN pg_database d ON l.database = d.oid 3 WHERE l.pid != pg_backend_pid() 4 ORDER BY l.pid; 5 pid | datname | locktype | relation | transactionid | mode | granted 6-----+---------+---------------+-----------+---------------+------------------+--------- 7 96 | root | relation | hoge_pkey | | RowExclusiveLock | t 8 96 | root | relation | hoge | | RowExclusiveLock | t 9 96 | | virtualxid | | | ExclusiveLock | t 10 96 | | transactionid | | 1701 | ExclusiveLock | t 11(4 rows) 12
  • TX Bにてinsert実行後のロック状態

=> TX A(id:1699)の完了を待っていることが、ShareLockがnot grantedであることからわかる

119 | | transactionid | | 1699 | ShareLock | f

psql

1> SELECT l.pid,d.datname,l.locktype,relation::regclass,transactionid,l.mode,l.granted 2 FROM pg_locks l LEFT JOIN pg_database d ON l.database = d.oid 3 WHERE l.pid != pg_backend_pid() 4 ORDER BY l.pid; 5 pid | datname | locktype | relation | transactionid | mode | granted 6-----+---------+---------------+-----------+---------------+------------------+--------- 7 96 | | virtualxid | | | ExclusiveLock | t 8 96 | | transactionid | | 1699 | ExclusiveLock | t 9 96 | root | relation | hoge_pkey | | RowExclusiveLock | t 10 96 | root | relation | hoge | | RowExclusiveLock | t 11 119 | root | relation | hoge_pkey | | RowExclusiveLock | t 12 119 | | transactionid | | 1699 | ShareLock | f 13 119 | root | relation | hoge | | RowExclusiveLock | t 14 119 | | virtualxid | | | ExclusiveLock | t 15 119 | | transactionid | | 1700 | ExclusiveLock | t 16(9 rows)
  • TX Aをcommitした直後のロック状態

psql

1> SELECT l.pid,d.datname,l.locktype,relation::regclass,transactionid,l.mode,l.granted 2 FROM pg_locks l LEFT JOIN pg_database d ON l.database = d.oid 3 WHERE l.pid != pg_backend_pid() 4 ORDER BY l.pid; 5 pid | datname | locktype | relation | transactionid | mode | granted 6-----+---------+---------------+----------+---------------+------------------+--------- 7 119 | root | relation | hoge | | RowExclusiveLock | t 8 119 | | virtualxid | | | ExclusiveLock | t 9 119 | | transactionid | | 1702 | ExclusiveLock | t 10(3 rows)

hoge_pkey は index

psql

1> select * from hoge_pkey; 2ERROR: "hoge_pkey" is an index

投稿2019/08/03 02:02

編集2019/08/03 02:10
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

システムカタログのpg_locksビューでロックモードの確認してみたらいかがでしょうか?

投稿2019/08/02 23:22

pea

総合スコア426

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/08/03 02:03

ご指摘ありがとうございます。上記にて確認結果を載せております。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問