Подготовительные действия

Устанавливаем необходимые расширения.

=> ALTER SYSTEM SET shared_preload_libraries = 'pg_wait_sampling';
ALTER SYSTEM
=> \q
postgres$ pg_ctl -w -l /home/postgres/logfile -D /usr/local/pgsql/data restart
waiting for server to shut down.... done
server stopped
waiting for server to start.... done
server started
student$ psql 
=> CREATE DATABASE locks_memory;
CREATE DATABASE
=> \c locks_memory
You are now connected to database "locks_memory" as user "postgres".
=> CREATE EXTENSION pg_wait_sampling;
CREATE EXTENSION
=> CREATE EXTENSION pg_buffercache;
CREATE EXTENSION

Таблица, как в предыдущих практиках.

=> CREATE TABLE accounts(acc_no integer, amount numeric);
CREATE TABLE
=> INSERT INTO accounts VALUES (1,1000.00),(2,2000.00),(3,3000.00);
INSERT 0 3

Закрепление буфера при открытом курсоре

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

=> BEGIN;
BEGIN
=> DECLARE c CURSOR FOR SELECT * FROM accounts;
DECLARE CURSOR
=> FETCH c;
 acc_no | amount  
--------+---------
      1 | 1000.00
(1 row)

Проверим, закреплен ли буфер:

=> SELECT * FROM pg_buffercache
WHERE relfilenode = pg_relation_filenode('accounts') AND relforknumber = 0 \gx
-[ RECORD 1 ]----+------
bufferid         | 324
relfilenode      | 25288
reltablespace    | 1663
reldatabase      | 25264
relforknumber    | 0
relblocknumber   | 0
isdirty          | t
usagecount       | 4
pinning_backends | 1

Да, pinning_backends = 1.

Очистка закрепленного буфера

Выполним очистку:

student$ psql locks_memory
=> VACUUM VERBOSE accounts;
INFO:  vacuuming "public.accounts"
INFO:  "accounts": found 0 removable, 0 nonremovable row versions in 1 out of 1 pages
DETAIL:  0 dead row versions cannot be removed yet, oldest xmin: 215351
There were 0 unused item pointers.
Skipped 1 page due to buffer pins, 0 frozen pages.
0 pages are entirely empty.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
INFO:  vacuuming "pg_toast.pg_toast_25288"
INFO:  index "pg_toast_25288_index" now contains 0 row versions in 1 pages
DETAIL:  0 index row versions were removed.
0 index pages have been deleted, 0 are currently reusable.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
INFO:  "pg_toast_25288": found 0 removable, 0 nonremovable row versions in 0 out of 0 pages
DETAIL:  0 dead row versions cannot be removed yet, oldest xmin: 215351
There were 0 unused item pointers.
Skipped 0 pages due to buffer pins, 0 frozen pages.
0 pages are entirely empty.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
VACUUM

Как мы видим, страница была пропущена: Skipped 1 page due to buffer pins.

Очистка не может обработать страницу: если буфер закреплен, из страницы запрещено удалять версии строк. Но очистка не ждет, пока буфер освободится - страница будет обработана при следующей очистке.

Заморозка закрепленного буфера

Выполняем очистку с заморозкой:

=> VACUUM FREEZE VERBOSE accounts;

Очистка зависает до закрытия курсора. При явно запрошенной заморозке нельзя пропустить ни одну страницу, не отмеченную в карте заморозки - иначе невозможно уменьшить максимальный возраст незамороженных транзакций в pg_class.relfrozenxid.

=> SELECT age(relfrozenxid) FROM pg_class WHERE oid = 'accounts'::regclass;
 age 
-----
   2
(1 row)

=> COMMIT;
COMMIT

INFO:  vacuuming "public.accounts"
INFO:  "accounts": found 0 removable, 3 nonremovable row versions in 1 out of 1 pages
DETAIL:  0 dead row versions cannot be removed yet, oldest xmin: 215351
There were 0 unused item pointers.
Skipped 0 pages due to buffer pins, 0 frozen pages.
0 pages are entirely empty.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 5.07 s.
INFO:  vacuuming "pg_toast.pg_toast_25288"
INFO:  index "pg_toast_25288_index" now contains 0 row versions in 1 pages
DETAIL:  0 index row versions were removed.
0 index pages have been deleted, 0 are currently reusable.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
INFO:  "pg_toast_25288": found 0 removable, 0 nonremovable row versions in 0 out of 0 pages
DETAIL:  0 dead row versions cannot be removed yet, oldest xmin: 215351
There were 0 unused item pointers.
Skipped 0 pages due to buffer pins, 0 frozen pages.
0 pages are entirely empty.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
VACUUM
=> SELECT age(relfrozenxid) FROM pg_class WHERE oid = 'accounts'::regclass;
 age 
-----
   0
(1 row)

Профиль ожиданий:

=> SELECT p.pid, a.backend_type, a.application_name AS app, p.event_type, p.event, p.count
FROM pg_wait_sampling_profile p
  LEFT JOIN pg_stat_activity a ON p.pid = a.pid
ORDER BY p.pid, p.count DESC;
  pid  |    backend_type     | app  | event_type |         event         | count 
-------+---------------------+------+------------+-----------------------+-------
 30476 | checkpointer        |      | Activity   | CheckpointerMain      |   601
 30476 | checkpointer        |      | IO         | DataFileSync          |     1
 30476 | checkpointer        |      | IO         | ControlFileSyncUpdate |     1
 30477 | background writer   |      | Activity   | BgWriterMain          |   604
 30478 | walwriter           |      | Activity   | WalWriterMain         |   604
 30479 | autovacuum launcher |      | Activity   | AutoVacuumMain        |   602
 30482 | background worker   |      | Activity   | LogicalLauncherMain   |   604
 30504 |                     |      | Client     | ClientRead            |     4
 30504 |                     |      | IO         | CopyFileWrite         |     2
 30528 | client backend      | psql | Client     | ClientRead            |   557
 30528 | client backend      | psql | IO         | DataFileRead          |     1
 30738 | client backend      | psql | BufferPin  | BufferPin             |   493
 30738 | client backend      | psql | Client     | ClientRead            |    31
(13 rows)

Тип ожидания BufferPin говорит о том, что очистка ждала освобождения буфера.