
БУФЕРНЫЙ КЭШ
~~~~~~~~~~~~

Создадим отдельную базу данных:

        => create database db7;
        CREATE DATABASE

        => \c db7
        You are now connected to database "db7" 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

pg_ctl restart -w -m fast -l /home/postgres/logfile 
waiting for server to shut down.... done
server stopped
waiting for server to start.... done
server started

.......................................................................

Проверим размер кэша:

        psql 

        => \c db7
        You are now connected to database "db7" as user "postgres".

        => 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       |          5
                2 | pg_authid                               | main    |              0 | f       |          5
               55 | pg_database_oid_index                   | main    |              0 | f       |          3
               56 | pg_database_oid_index                   | main    |              1 | f       |          3
               57 | pg_db_role_setting_databaseid_rol_index | main    |              0 | f       |          5
               58 | pg_authid_rolname_index                 | main    |              0 | f       |          2
               59 | pg_authid_rolname_index                 | main    |              1 | f       |          2
               60 | pg_authid_oid_index                     | main    |              0 | f       |          2
               61 | pg_authid_oid_index                     | main    |              1 | f       |          2
               62 | pg_database_datname_index               | main    |              0 | f       |          2
               63 | pg_database_datname_index               | main    |              1 | f       |          4
               64 | pg_class                                | main    |              0 | t       |          5
               65 | pg_class                                | main    |              1 | f       |          5
               66 | pg_class                                | main    |              2 | f       |          5
               67 | pg_attribute                            | main    |              0 | f       |          5
               68 | pg_attribute                            | main    |              1 | f       |          5
               69 | pg_attribute                            | main    |              2 | f       |          5
               70 | pg_attribute                            | main    |              3 | f       |          5
               71 | pg_attribute                            | main    |              4 | f       |          5
               72 | pg_attribute                            | main    |              5 | f       |          5
        (20 rows)
        

.......................................................................

Используя это расширение, можно наблюдать за состоянием кэша с разных сторон.
Кроме информации о том, как представлена в кэше та или иная страница,
можно, например, посмотреть распределение буферов по их "популярности":

        => select usagecount,
        =>        count(*)
        => from   pg_buffercache
        => group by usagecount
        => order by usagecount;
         usagecount | count 
        ------------+-------
                  1 |    95
                  2 |    42
                  3 |     8
                  4 |    15
                  5 |   140
                    |   212
        (6 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 
        ----------+---------+---------+----------------+---------+------------
              304 | test    | main    |              0 | f       |          1
              305 | test    | main    |              1 | f       |          1
              306 | test    | main    |              2 | f       |          1
        (3 rows)
        

Расширение pg_prewarm может также использоваться для прогрева кэша
операционной системы.

.......................................................................

Изменим одну строку таблицы (происходит удаление и вставка):

        => 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 
        ----------+---------+---------+----------------+---------+------------
              304 | test    | main    |              0 | t       |          2
              305 | test    | main    |              1 | f       |          2
              306 | test    | main    |              2 | t       |          3
              308 | test    | vm      |              0 | t       |          1
              309 | test    | fsm     |              0 | f       |          1
              310 | test    | fsm     |              1 | f       |          1
              311 | test    | fsm     |              2 | f       |          1
        (7 rows)
        

.......................................................................

Следует учитывать, что расширение pg_buffercache накладывает блокировки
на время доступа к кэшу, поэтому не следует использовать его слишком часто
на продуктивной среде.

.......................................................................

ПРОЦЕСС ФОНОВОЙ ЗАПИСИ
~~~~~~~~~~~~~~~~~~~~~~

Работа фонового процесса записи отражается в собираемой статистике;
за ней можно наблюдать с помощью представления pg_stat_bgwriter.
Часть полей этого представления относится и к процессу checkpointer.
Пока мы не будем обращать на них внимание.

        => select buffers_clean, maxwritten_clean, buffers_backend from pg_stat_bgwriter;
         buffers_clean | maxwritten_clean | buffers_backend 
        ---------------+------------------+-----------------
                     0 |                0 |              71
        (1 row)
        

* buffers_clean    - количество буферов, записанных на диск процессом writer,
* maxwritten_clean - количество остановок по достижению bgwriter_lru_maxpages,
* buffers_backend  - количество буферов, записанных серверными процессами.

.......................................................................

Дадим нагрузку на базу данных.
Для этого удобно воспользоваться расширением pgbench. Оно позволяет прогонять
тест TPC-B или любой пользовательский сценарий.

Сначала надо создать таблицы для теста:

pgbench -i db7 
NOTICE:  table "pgbench_history" does not exist, skipping
NOTICE:  table "pgbench_tellers" does not exist, skipping
NOTICE:  table "pgbench_accounts" does not exist, skipping
NOTICE:  table "pgbench_branches" does not exist, skipping
creating tables...
100000 of 100000 tuples (100%) done (elapsed 0.12 s, remaining 0.00 s)
vacuum...
set primary keys...
done.

.......................................................................

Для чистоты эксперимента выполним контрольную точку, чтобы сбросить все
грязные буферы на диск, и сбросим статистику.

        => checkpoint;
        CHECKPOINT

        => select pg_stat_reset_shared('bgwriter');
         pg_stat_reset_shared 
        ----------------------
         
        (1 row)
        

.......................................................................

Теперь можно запускать сам тест.
Установим длительность 30 секунд и скорость 100 транзакций в секунду.


pgbench -T 30 -R 100 -P 1 db7 
starting vacuum...end.
progress: 1.0 s, 102.0 tps, lat 2.523 ms stddev 0.977, lag 0.608 ms
progress: 2.0 s, 99.0 tps, lat 3.867 ms stddev 5.540, lag 1.495 ms
progress: 3.0 s, 98.0 tps, lat 2.379 ms stddev 0.821, lag 0.413 ms
progress: 4.0 s, 103.0 tps, lat 2.393 ms stddev 1.207, lag 0.506 ms
progress: 5.0 s, 108.0 tps, lat 2.458 ms stddev 0.847, lag 0.558 ms
progress: 6.0 s, 91.0 tps, lat 2.229 ms stddev 0.482, lag 0.359 ms
progress: 7.0 s, 88.0 tps, lat 2.649 ms stddev 3.175, lag 0.510 ms
progress: 8.0 s, 91.0 tps, lat 2.331 ms stddev 1.031, lag 0.511 ms
progress: 9.0 s, 88.0 tps, lat 2.395 ms stddev 0.871, lag 0.502 ms
progress: 10.0 s, 91.0 tps, lat 2.358 ms stddev 0.804, lag 0.388 ms
progress: 11.0 s, 100.0 tps, lat 2.304 ms stddev 0.568, lag 0.444 ms
progress: 12.0 s, 98.0 tps, lat 2.412 ms stddev 1.891, lag 0.437 ms
progress: 13.0 s, 94.0 tps, lat 2.187 ms stddev 0.502, lag 0.303 ms
progress: 14.0 s, 105.0 tps, lat 3.594 ms stddev 5.307, lag 1.482 ms
progress: 15.0 s, 114.0 tps, lat 2.158 ms stddev 0.452, lag 0.318 ms
progress: 16.0 s, 97.0 tps, lat 2.251 ms stddev 0.572, lag 0.397 ms
progress: 17.0 s, 80.0 tps, lat 2.761 ms stddev 2.148, lag 0.537 ms
progress: 18.0 s, 110.0 tps, lat 2.119 ms stddev 0.475, lag 0.340 ms
progress: 19.0 s, 103.0 tps, lat 2.273 ms stddev 0.731, lag 0.436 ms
progress: 20.0 s, 99.0 tps, lat 2.277 ms stddev 0.783, lag 0.483 ms
progress: 21.0 s, 93.0 tps, lat 2.237 ms stddev 0.667, lag 0.430 ms
progress: 22.0 s, 94.0 tps, lat 2.559 ms stddev 1.519, lag 0.499 ms
progress: 23.0 s, 105.0 tps, lat 2.113 ms stddev 0.679, lag 0.356 ms
progress: 24.0 s, 106.0 tps, lat 4.159 ms stddev 9.069, lag 1.868 ms
progress: 25.0 s, 85.0 tps, lat 2.266 ms stddev 0.606, lag 0.373 ms
progress: 26.0 s, 97.0 tps, lat 2.348 ms stddev 1.151, lag 0.554 ms
progress: 27.0 s, 88.0 tps, lat 2.393 ms stddev 2.446, lag 0.330 ms
progress: 28.0 s, 104.0 tps, lat 2.189 ms stddev 0.625, lag 0.411 ms
progress: 29.0 s, 87.0 tps, lat 2.283 ms stddev 0.762, lag 0.373 ms
progress: 30.0 s, 93.0 tps, lat 2.286 ms stddev 0.626, lag 0.476 ms
transaction type: TPC-B (sort of)
scaling factor: 1
query mode: simple
number of clients: 1
number of threads: 1
duration: 30 s
number of transactions actually processed: 2912
latency average: 2.496 ms
latency stddev: 2.558 ms
rate limit schedule lag: avg 0.564 (max 52.842) ms
tps = 97.064247 (including connections establishing)
tps = 97.070999 (excluding connections establishing)

.......................................................................

        => select buffers_clean, maxwritten_clean, buffers_backend from pg_stat_bgwriter;
         buffers_clean | maxwritten_clean | buffers_backend 
        ---------------+------------------+-----------------
                  3037 |                7 |             168
        (1 row)
        

По полученной информации видно, что основную работу выполнял процесс writer,
хотя и серверным процессам пришлось немного поработать.

.......................................................................

Конец демонстрации.

.......................................................................

        => alter system reset all;
        ALTER SYSTEM

        => \q

pg_ctl restart -w -m fast -l /home/postgres/logfile 
waiting for server to shut down.... done
server stopped
waiting for server to start.... done
server started
