=> CREATE DATABASE wal_buffercache;
CREATE DATABASE
=> \c wal_buffercache
You are now connected to database "wal_buffercache" as user "postgres".
Создадим таблицу:
=> CREATE TABLE test(t text) WITH (autovacuum_enabled = off);
CREATE TABLE
=> INSERT INTO test SELECT 'line '||s.id FROM generate_series(1,500) AS s(id);
INSERT 0 500
=> VACUUM ANALYZE test;
VACUUM
Уменьшим временно размер буферного кэша, чтобы упростить наблюдение за ним.
=> ALTER SYSTEM SET shared_buffers = 512;
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 wal_buffercache
=> SELECT setting, unit FROM pg_settings WHERE name = 'shared_buffers';
setting | unit ---------+------ 512 | 8kB (1 row)
Содержимое буферного кэша можно посмотреть с помощью расширения:
=> CREATE EXTENSION pg_buffercache;
CREATE EXTENSION
Создадим для удобства представление:
=> CREATE VIEW pg_buffercache_v AS SELECT bufferid, (SELECT c.relname FROM pg_class c WHERE pg_relation_filenode(c.oid) = b.relfilenode ) relname, CASE relforknumber WHEN 0 THEN 'main' WHEN 1 THEN 'fsm' WHEN 2 THEN 'vm' END relfork, relblocknumber, isdirty, usagecount FROM pg_buffercache b WHERE b.reldatabase IN ( 0, (SELECT oid FROM pg_database WHERE datname = current_database()) ) AND b.usagecount is not null;
CREATE VIEW
Условие на базу данных необходимо, так как в буферном кэше содержатся данные всего кластера. Расшифровать мы можем только информацию из той БД, к которой подключены. Глобальные объекты считаются принадлежащими БД с нулевым OID.
=> SELECT * FROM pg_buffercache_v LIMIT 20;
bufferid | relname | relfork | relblocknumber | isdirty | usagecount ----------+--------------+---------+----------------+---------+------------ 1 | pg_database | main | 0 | f | 4 2 | pg_authid | main | 0 | f | 3 3 | pg_class | main | 0 | t | 5 4 | pg_class | main | 1 | f | 5 5 | pg_class | main | 2 | f | 5 6 | pg_attribute | main | 0 | f | 5 7 | pg_attribute | main | 1 | f | 5 8 | pg_attribute | main | 2 | f | 5 9 | pg_attribute | main | 3 | f | 5 10 | pg_attribute | main | 4 | f | 5 11 | pg_attribute | main | 5 | f | 5 12 | pg_attribute | main | 6 | f | 5 13 | pg_attribute | main | 7 | f | 5 14 | pg_attribute | main | 8 | f | 5 15 | pg_attribute | main | 9 | f | 5 16 | pg_attribute | main | 10 | f | 5 17 | pg_attribute | main | 11 | f | 5 18 | pg_attribute | main | 12 | f | 5 19 | pg_attribute | main | 13 | f | 5 20 | pg_attribute | main | 14 | f | 5 (20 rows)
Используя это расширение, можно наблюдать за состоянием кэша с разных сторон.
Кроме информации о том, как представлена в кэше та или иная страница, можно, например, посмотреть распределение буферов по их "популярности":
=> SELECT usagecount, count(*) FROM pg_buffercache GROUP BY usagecount ORDER BY usagecount;
usagecount | count ------------+------- 1 | 104 2 | 31 3 | 10 4 | 13 5 | 98 | 256 (6 rows)
Можно посмотреть, какая доля каких таблиц закэширована (и насколько активно используются эти данные):
=> SELECT c.relname, count(*) blocks, round( 100.0 * 8192 * count(*) / pg_table_size(c.oid) ) "% of rel", round( 100.0 * 8192 * count(*) FILTER (WHERE b.usagecount > 3) / pg_table_size(c.oid) ) "% hot" FROM pg_buffercache b JOIN pg_class c ON pg_relation_filenode(c.oid) = b.relfilenode WHERE b.reldatabase IN ( 0, (SELECT oid FROM pg_database WHERE datname = current_database()) ) AND b.usagecount is not null GROUP BY c.relname, c.oid ORDER BY 2 DESC LIMIT 10;
relname | blocks | % of rel | % hot --------------------------------+--------+----------+------- pg_attribute | 28 | 56 | 48 pg_proc | 17 | 22 | 4 pg_depend_reference_index | 15 | 41 | 27 pg_class | 14 | 93 | 73 pg_depend | 13 | 24 | 11 pg_operator | 10 | 56 | 44 pg_type | 10 | 77 | 8 pg_proc_oid_index | 8 | 89 | 44 pg_proc_proname_args_nsp_index | 7 | 23 | 6 pg_rewrite | 7 | 9 | 1 (10 rows)
Подобные запросы могут подсказать, насколько активно используется буферный кэш и дать пищу для размышлений о том, стоит ли увеличивать или уменьшать его размер.
Надо учитывать, что такие запросы:
После перезапуска сервера должно пройти некоторое время, чтобы кэш "прогрелся" - набрал актуальные активно использующиеся данные. Иногда может оказаться полезным сразу прочитать в кэш данные определенных таблиц.
Посмотрим на расширение для прогрева кэша:
=> CREATE EXTENSION pg_prewarm;
CREATE EXTENSION
Например, сейчас в кэше нет таблицы test (мы перезагружали сервер):
=> SELECT * FROM pg_buffercache_v WHERE relname = 'test';
bufferid | relname | relfork | relblocknumber | isdirty | usagecount ----------+---------+---------+----------------+---------+------------ (0 rows)
В простейшем виде вызов функции считывает указанную таблицу в буферный кэш.
=> SELECT pg_prewarm('test');
pg_prewarm ------------ 3 (1 row)
=> SELECT * FROM pg_buffercache_v WHERE relname = 'test';
bufferid | relname | relfork | relblocknumber | isdirty | usagecount ----------+---------+---------+----------------+---------+------------ 278 | test | main | 0 | f | 1 279 | test | main | 1 | f | 1 280 | test | main | 2 | f | 1 (3 rows)
Изменим одну строку таблицы:
=> UPDATE test SET t = 'changed' WHERE t = 'line 1';
UPDATE 1
=> SELECT * FROM pg_buffercache_v WHERE relname = 'test';
bufferid | relname | relfork | relblocknumber | isdirty | usagecount ----------+---------+---------+----------------+---------+------------ 278 | test | main | 0 | t | 2 279 | test | main | 1 | f | 2 280 | test | main | 2 | t | 3 283 | test | vm | 0 | t | 1 284 | test | fsm | 0 | f | 1 285 | test | fsm | 1 | f | 1 286 | test | fsm | 2 | f | 1 (7 rows)
Как объяснить такой результат?
Изменились две табличные страницы (на одной версия строки удалена, в другую - добавлена), в кэш прочитана карта свободного пространства и карта видимости (которая, к тому же, изменилась).
Следует учитывать, что расширение pg_buffercache накладывает блокировки на время доступа к кэшу, поэтому не следует использовать его слишком часто на производственном сервере.
Расширение pg_prewarm может использоваться и для прогрева кэша операционной системы. А в версии PostgreSQL 11 оно получит возможность сохранять актуальное состояние кэша на диск и восстанавливать его же после перезагрузки сервера.
Конец демонстрации.