意外と意識されていないPostgreSQLのロックについて


開発でPostgreSQLを利用することはよくありますよね。
しかし、ロックについてしっかり理解している人は少ないと思います。

PostgreSQLのロックの種類

まず、PostgreSQLのロックには以下のようなロックレベルがあります。

  • ACCESS SHARE
  • ROW SHARE
  • ROW EXCLUSIVE
  • SHARE UPDATE EXCLUSIVE
  • SHARE
  • SHARE ROW EXCLUSIVE
  • EXCLUSIVE
  • ACCESS EXCLUSIVE

それぞれについての詳しい内容は公式ドキュメントをご覧ください
https://www.postgresql.jp/document/12/html/explicit-locking.html

よく使うSQLだと例えば、
SELECT文はACCESS SHARE、INSERTやUPDATEはROW EXCLUSIVEにあたります。

競合するロック

ロックは以下の表の☓がの部分が競合します。

ACCESS SHARE ROW SHARE ROW EXCLUSIVE SHARE UPDATE EXCLUSIVE SHARE SHARE ROW EXCLUSIVE EXCLUSIVE ACCESS EXCLUSIVE
ACCESS SHARE X
ROW SHARE X X
ROW EXCLUSIVE X X X X
SHARE UPDATE EXCLUSIVE X X X X X
SHARE X X X X X X
SHARE ROW EXCLUSIVE X X X X X X
EXCLUSIVE X X X X X X X
ACCESS EXCLUSIVE X X X X X X X X

ACCESS EXCLUSIVEロック時に、ACCESS SHAREの実行はロックされ待ちが発生します。
しかし、ROW EXCLUSIVEロック時に、ACCESS SHAREの実行はロックされず実行することが可能です。

実際に実行してみる

競合なしパターン

BEGIN;

INSERT INTO hoge VALUES (1, 'hoge');

を実行して、ロック状況を見ると以下のようになります(pgAdminで確認してます。)

RowExclusiveLockでロックされているのがわかると思います。
この状態で以下を実行すると実行可能なことがわかります。
実行する際にはさきほど実行したのとは別のセッションでお願いします。

SELECT * FROM hoge LIMIT 1;

競合ありパターン

BEGIN;

TRUNCATE TABLE hoge;

を実行して、ロック状況を見ると以下のようになります。

こちらはAccessExclusiveLockになっているのがわかると思います。
この状態でさきほどと同じ以下のSQLを実行してみてください。

SELECT * FROM hoge LIMIT 1;

おそらくすぐに実行されないと思います。
この状態でロックかけてる側のセッションで

ROLLBACK;

を実行するとSELECTの結果も返ってくると思います。

まとめ

このように普段意外と意識していないかもしれませんが、しっかりとロックレベルがあります。
これを意識して開発するのは大事ですね。ロックレベルを意識的に上げたい場合などは明示的にロックをすることも可能なのでそのへんも意識していけるといいですね。