Table locking modes

Table locks are often acquired automatically, but they can also be acquired explicitly with the LOCK command. The following is the list of locking modes:

  • ACCESS SHARE: This mode is acquired by the SELECT statement.
  • ROW SHARE: The SELECT FOR UPDATE and SELECT FOR SHARE commands acquire this lock.
  • ROW EXCLUSIVE: The statements UPDATE, DELETE, and INSERT acquire this lock mode.
  • SHARE UPDATE EXCLUSIVE: This mode is used to protect a table against concurrent schema changes. Acquired by VACUUM (without FULL), ANALYZE, CREATE INDEX CONCURRENTLY, CREATE STATISTICS, and ALTER TABLE VALIDATE and other ALTER TABLE variants.
  • SHARE: This mode is used to protect a table against concurrent data changes. Acquired by CREATE INDEX (without CONCURRENTLY).
  • SHARE ROW EXCLUSIVE: This mode protects a table against concurrent data changes, and is self-exclusive, so that only one session can hold it at a time. Acquired by CREATE COLLATION, CREATE TRIGGER, and many forms of ALTER TABLE.
  • EXCLUSIVE: This is acquired by REFRESH MATERIALIZED VIEW CONCURRENTLY. This mode only allows reading data for the table. 
  • ACCESS EXCLUSIVE: This mode guarantees that the holder is the only transaction accessing the table in any way. Acquired by the DROP TABLE, TRUNCATE, REINDEX, CLUSTER, VACUUM FULL, and REFRESH MATERIALIZED VIEW (without CONCURRENTLY) commands. Many forms of ALTER TABLE also acquire a lock at this level. This is also the default lock mode for LOCK TABLE statements that do not specify a mode explicitly.

A very important thing to note for each mode is the list of modes that are in conflict with that specific mode. Transactions can't hold locks on conflicting modes on the same table.

As shown in the preceding table, ACCESS EXCLUSIVE conflicts with ACCESS SHARE, which means one cannot perform the SELECT statement on a table if the table is locked in ACCESS EXCLUSIVE mode. Also, one cannot DROP a table if one is reading it. Also note that one can execute SELECT statements in all other modes. So the only mode where a SELECT statement can be blocked is the ACCESS EXCLUSIVE mode.

The following example shows what will happen in the case of dropping a table while another transaction is reading from it:

Session 1

Session 2

T1

BEGIN;
BEGIN
postgres=# SELECT COUNT(*) FROM test_tx_level ;
count
-------
3
(1 row)

postgres=# SELECT mode, granted FROM pg_locks where relation ='test_tx_level'::regclass::oid;
mode | granted
-----------------+---------
AccessShareLock | t
(1 row)

T2

BEGIN
postgres=# DROP TABLE test_tx_level;

T3

postgres=# SELECT mode, granted FROM pg_locks where relation ='test_tx_level'::regclass::oid;
mode | granted
---------------------+---------
AccessShareLock | t
AccessExclusiveLock | f
(2 rows)

 

The preceding example shows that the lock is acquired when the data is selected from the table in session 1 and they are not released. So, once a lock is acquired, the lock is normally held till the end of the transaction. The pg_locks table is very handy for understanding locks. It is often used to detect bottlenecks in high concurrency systems. The following view shows lock information in a human-friendly way:


CREATE OR REPLACE VIEW lock_info AS
SELECT
lock1.pid as locked_pid,
stat1.usename as locked_user,
stat1.query as locked_statement,
stat1.state as locked_statement_state,
stat2.query as locking_statement,
stat2.state as locking_statement_state,
now() - stat1.query_start as locking_duration,
lock2.pid as locking_pid,
stat2.usename as locking_user
FROM pg_catalog.pg_locks lock1
JOIN pg_catalog.pg_stat_activity stat1 on lock1.pid = stat1.pid
JOIN pg_catalog.pg_locks lock2 on
(lock1.locktype,lock1.database,lock1.relation,lock1.page,lock1.tuple,lock1.virtualxid,lock1.transactionid,lock1.classid,lock1.objid,lock1.objsubid) IS NOT DISTINCT FROM
(lock2.locktype,lock2.DATABASE,lock2.relation,lock2.page,lock2.tuple,lock2.virtualxid,lock2.transactionid,lock2.classid,lock2.objid,lock2.objsubid)
JOIN pg_catalog.pg_stat_activity stat2 on lock2.pid = stat2.pid
WHERE NOT lock1.granted AND lock2.granted;

To check the locks: 

postgres=# SELECT * FROM lock_info ;
-[ RECORD 1 ]-----+-----------------------------------------------------------------------------------
locked_pid | 3736
locked_user | postgres
locked_statement | DROP TABLE test_tx_level;
locked_statement_state | active
locking_statement | SELECT mode, granted FROM pg_locks where relation ='test_tx_level'::regclass::oid;
locking_statement_state| idle in transaction
locking_duration | 00:09:57.32628
locking_pid | 3695
locking_user | postgres

The view shows that the process 3736 tries to execute the DROP TABLE statement and is waiting for the transaction issued by the process ID 3695. Since the process 3695 is doing nothing at the time of running the preceding query, the state of the process is idle in transaction. Finally, the preceding view is a bit misleading, as it shows the locking statement is SELECT mode, granted ..; which is not true. Simply, the pg_state_activity shows the last statement executed by the process. As we said earlier, once the lock is acquired, it is held by the transaction. So the process 3736 is waiting for the transaction started by process 3695 to finish. 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset