Блокировки читающей транзакции, Read Committed

Создадим таблицу как в демонстрации; номер счета будет первичным ключом.

=> CREATE DATABASE locks_objects;
CREATE DATABASE
=> \c locks_objects
You are now connected to database "locks_objects" as user "postgres".
=> CREATE TABLE accounts(acc_no integer PRIMARY KEY, amount numeric);
CREATE TABLE
=> INSERT INTO accounts VALUES (1,1000.00),(2,2000.00),(3,3000.00);
INSERT 0 3

Начинаем транзакцию и читаем одну строку.

=> \c locks_objects
You are now connected to database "locks_objects" as user "postgres".
=> SELECT pg_backend_pid();
 pg_backend_pid 
----------------
          28663
(1 row)

=> BEGIN;
BEGIN
=> SELECT * FROM accounts WHERE acc_no = 1;
 acc_no | amount  
--------+---------
      1 | 1000.00
(1 row)

Блокировки:

=> SELECT locktype, relation::REGCLASS, virtualxid AS virtxid, transactionid AS xid, mode, granted
FROM pg_locks WHERE pid = 28663;
  locktype  |   relation    | virtxid | xid |      mode       | granted 
------------+---------------+---------+-----+-----------------+---------
 relation   | accounts_pkey |         |     | AccessShareLock | t
 relation   | accounts      |         |     | AccessShareLock | t
 virtualxid |               | 2/75    |     | ExclusiveLock   | t
(3 rows)

Здесь мы видим:

Если смотреть блокировки в самой транзакции, к ним добавится блокировка на таблицу pg_locks:

=> SELECT locktype, relation::REGCLASS, virtualxid AS virtxid, transactionid AS xid, mode, granted
FROM pg_locks WHERE pid = 28663;
  locktype  |   relation    | virtxid | xid |      mode       | granted 
------------+---------------+---------+-----+-----------------+---------
 relation   | pg_locks      |         |     | AccessShareLock | t
 relation   | accounts_pkey |         |     | AccessShareLock | t
 relation   | accounts      |         |     | AccessShareLock | t
 virtualxid |               | 2/75    |     | ExclusiveLock   | t
(4 rows)

=> COMMIT;
COMMIT

Блокировки читающей транзакции, Serializable

Начинаем транзакцию и читаем одну строку.

=> BEGIN ISOLATION LEVEL SERIALIZABLE;
BEGIN
=> SELECT * FROM accounts WHERE acc_no = 1;
 acc_no | amount  
--------+---------
      1 | 1000.00
(1 row)

Блокировки:

=> SELECT locktype, relation::REGCLASS, page, tuple, virtualxid AS virtxid, transactionid AS xid, mode, granted
FROM pg_locks WHERE pid = 28663;
  locktype  |   relation    | page | tuple | virtxid | xid |      mode       | granted 
------------+---------------+------+-------+---------+-----+-----------------+---------
 relation   | accounts_pkey |      |       |         |     | AccessShareLock | t
 relation   | accounts      |      |       |         |     | AccessShareLock | t
 virtualxid |               |      |       | 2/76    |     | ExclusiveLock   | t
 page       | accounts_pkey |    1 |       |         |     | SIReadLock      | t
 tuple      | accounts      |    0 |     1 |         |     | SIReadLock      | t
(5 rows)

К предыдущим блокировкам добавились:

=> COMMIT;
COMMIT

Вывести в журнал информацию о блокировках

Требуется изменить параметры:

=> ALTER SYSTEM SET log_lock_waits = on;
ALTER SYSTEM
=> ALTER SYSTEM SET deadlock_timeout = '100ms';
ALTER SYSTEM
=> SELECT pg_reload_conf();
 pg_reload_conf 
----------------
 t
(1 row)

Воспроизведем блокировку.

=> BEGIN;
BEGIN
=> UPDATE accounts SET amount = 10.00 WHERE acc_no = 1;
UPDATE 1
=> BEGIN;
BEGIN
=> UPDATE accounts SET amount = 100.00 WHERE acc_no = 1;

В первом сеансе выполним задержку и после этого завершим транзакцию.

=> SELECT pg_sleep(1);
 pg_sleep 
----------
 
(1 row)

=> COMMIT;
COMMIT

UPDATE 1
=> COMMIT;
COMMIT

Вот что попало в журнал:

postgres$ tail -n 7 /home/postgres/logfile
2019-08-12 17:25:51.399 MSK [28663] LOG:  process 28663 still waiting for ShareLock on transaction 215332 after 100.319 ms
2019-08-12 17:25:51.399 MSK [28663] DETAIL:  Process holding the lock: 28610. Wait queue: 28663.
2019-08-12 17:25:51.399 MSK [28663] CONTEXT:  while updating tuple (0,1) in relation "accounts"
2019-08-12 17:25:51.399 MSK [28663] STATEMENT:  UPDATE accounts SET amount = 100.00 WHERE acc_no = 1;
2019-08-12 17:25:52.369 MSK [28663] LOG:  process 28663 acquired ShareLock on transaction 215332 after 1070.271 ms
2019-08-12 17:25:52.369 MSK [28663] CONTEXT:  while updating tuple (0,1) in relation "accounts"
2019-08-12 17:25:52.369 MSK [28663] STATEMENT:  UPDATE accounts SET amount = 100.00 WHERE acc_no = 1;