2016-11-22 36 views
3

ロックがPostgresのトランザクションとどのようにやりとりするのか理解できません。Postgresがトランザクション内でロックする

私はこの(長い)クエリを実行すると、私は起こるロックの高度さに驚いています:\COPYため​​は、それが必要とロックのレベルは言及しませんが、this postが示す

BEGIN; 
TRUNCATE foo; 
\COPY foo FROM 'backup.txt'; 
COMMIT; 

RowExclusiveLockを取得するだけです。私は\COPY中にこのクエリを実行するときしかし:

SELECT mode, granted FROM pg_locks 
WHERE relation='foo'::regclass::oid; 

私はこれを取得:

一体AccessExclusiveLockから来ていることである
mode granted 
RowExclusiveLock true 
ShareLock true 
AccessExclusiveLock true 

?私はそれがTRUNCATEから来ていると仮定します。それはrequires an AccessExclusiveLockです。しかし、TRUNCATEはすばやく終了するので、ロックもすぐに解放されると思います。これは私にいくつかの質問を残す。

トランザクション内のコマンドによってロックが取得された場合、コマンドの終了時(トランザクションの終了前)にロックが解除されますか?もしそうなら、なぜ私は上記の行動を観察するのですか?そうでない場合は、どうしてですか?実際、transactions don't touch the table until the COMMIT以降、トランザクションでTRUNCATEはなぜテーブルをブロックする必要がありますか?

PGのtransactionsについてはdocumentationでこれ以上の議論はありません。

答えて

8

ここではいくつかの誤解を解消する必要があります。

最初に、トランザクションにコミットされます。引用しているコメントには、ROLLBACK(およびCOMMITも同様です)がテーブルに触れていないことが記載されています。これは異なるものです。彼らはトランザクションの状態をコミットログpg_clog)に記録し、COMMITはトランザクションログをディスクにフラッシュします(これに関する注目すべき例外は、質問に関連するTRUNCATEです。古いテーブルは、トランザクションはCOMMITの間に削除されます)。

COMMITまですべての変更が保持され、ロックが取られない場合、COMMITは非常に高価であり、同時変更のために日常的に失敗します。トランザクションは以前と同じようにデータベースの状態を覚えておき、変更がまだ適用されているかどうかを確認する必要があります。この並行処理の方法はoptimistic concurreny controlと呼ばれ、アプリケーションにとってまともな戦略ですが、COMMITが効率的で、失敗しないはずのリレーショナルデータベースではうまく機能しません(インフラストラクチャに大きな問題がない限り) 。

それでは、リレーショナルデータベースが使用することは、彼らは彼らの邪魔になってからの同時活動を防ぐためにそれにアクセスする前に、すなわち、彼らは、データベース・オブジェクトをロックし、をロック悲観的同時実行制御です。

、リレーショナルデータベースは、ロックが(少なくともユーザに見える、いわゆるヘビーロックが)常にトランザクションの終了まで保持されているtwo-phase locking、使用します。 これは、トランザクションを論理的な順序(シリアライズ可能)で一貫性のあるものにするために必要です(ただし、これで十分ではありません)。ロックを解除し、挿入されたコミットされていない行が外部キー制約を参照している行を削除した場合はどうなりますか?質問へ

回答

このすべての結論は、あなたのテーブルには、トランザクションが終了するまでTRUNCATEからACCESS EXCLUSIVEロックを保つことです。それがなぜ必要なのかはっきりしていませんか? TRUNCATEの後(まだコミットされていない)にテーブルを読み取ることが他のトランザクションで許可されていれば、TRUNCATEは本当にテーブルを空にし、MVCCのセマンティクスを遵守しないので、空であると見なします。このようなdirty read(まだロールバックされていないコミットされていないデータの)は許可できません。

実際に詰め替え中にテーブルへの読み取りアクセスが必要な場合は、TRUNCATEの代わりにを使用できます。欠点は、これが多くの空のスペース(テーブルbloat)の多くの結果、自動バキュームによって削除する必要がある多くの“死んだタプル”テーブルを残すはるかに高価な操作です。しかし、テーブルと索引のスキャンが少なくとも2倍の時間を要するような肥大化した表と索引を使用して喜んでいれば、それはオプションです。

+0

ありがとう、@Laurenz!私はトランザクション内の 'TRUNCATE'がどのように動作するのかを突き止めるためにまだ苦労しています。 "TRUNCATE"が実際にテーブルを空にし、 "ROLLBACK"がテーブルに触れていない "場合、TRUNCATEはどのようにロールバックされますか? –

+0

「ダーティーリード」の意味を理解することも難しいです([docs](https://www.postgresql.org/docs/current/static/transaction-iso.html)に記載されています)それは恐らくそれ自身の疑問を正当化するだろう。 –

+0

私は「ダーティリード」と説明し、リンクを追加しました。あなたは、私の説明が「TRUNCATE」に関して少し矛盾しているのは間違いありません。 [コードの説明](https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/commands/tablecmds.c;h=f97bee5b0e464265e2af103053800900f40058ff;hb=HEAD #l1214)、古いテーブルはすぐに破棄されるのではなく、コミット時に破棄されるので、 'COMMIT'はテーブルに正確に触れるのではなく、削除します。私は私の説明でそれをより明確にしようとします。 –

関連する問題