Перейти к основному содержимому

Уровень 3.0

Предусловия:

  • Изучена лекция 8 «Журнал предзаписи»

Анализ ведения журнала предзаписи​

  1. Откройте терминал и подключитесь в psql к базе данных postgres c ролью postgres:

    [student@pkles-gt0040964 ~]$ sudo -iu postgres
    -bash-4.4$ psql
    psql (15.5)
    Введите "help", чтобы получить справку.
  2. Создайте базу данных wal_db и подключитесь к ней:

    postgres=# CREATE DATABASE wal_db;
    CREATE DATABASE
    postgres=# \c wal_db
    Вы подключены к базе данных "wal_db" как пользователь "postgres".
  3. Создайте расширение pageinspect:

    wal_db=# CREATE EXTENSION pageinspect;
    CREATE EXTENSION

    Вспомним, что расширение pageinspect позволяет посмотреть содержимое страниц данных.

  4. Создайте расширение pg_walinspect:

    wal_db=# CREATE EXTENSION pg_walinspect;
    CREATE EXTENSION

    Расширение pg_walinspect позволяет посмотреть содержимое записей WAL.

  5. Создайте таблицу some_table:

    wal_db=# CREATE TABLE some_table(id integer, value integer) WITH (autovacuum_enabled=false);
    CREATE TABLE

    Таблица создана с параметром хранения autovacuum_enabled = false, чтобы автоочистка не мешала проведению эксперимента.

Операции в журнале на уровне minimal​

  1. Проверьте уровень журнала предзаписи:

    wal_db=# SHOW wal_level;
    wal_level
    -----------
    replica
    (1 строка)

    Уровень журнала предзаписи по умолчанию replica.

  2. Измените значения параметров max_wal_senders и wal_level:

    wal_db=# ALTER SYSTEM SET max_wal_senders = 0;
    ALTER SYSTEM
    wal_db=# ALTER SYSTEM SET wal_level = minimal;
    ALTER SYSTEM

    При установке значения параметра wal_level в minimal также было сброшено в 0 значение параметра max_wal_senders. Это потребовалось, поскольку на уровне minimal журнал не может использоваться для потоковой репликации, то есть не может использоваться процессами walsender.

  3. Перезапустите экземпляр Pangolin и проверьте значения установленных параметров:

    wal_db=# \q
    -bash-4.4$ pg_ctl restart -l logfile
    ожидание завершения работы сервера.... готово
    сервер остановлен
    ожидание запуска сервера.... готово
    сервер запущен
    -bash-4.4$ psql -d wal_db
    psql (15.5)
    Введите "help", чтобы получить справку.
    wal_db=# SELECT name, setting FROM pg_settings WHERE name IN ('wal_level', 'max_wal_senders');
    name | setting
    -----------------+---------
    max_wal_senders | 0
    wal_level | minimal
    (2 строки)

    Перезапуск экземпляра потребовался для применения установленных значений параметров.

  4. Сохраните в переменной psql позицию вставки журнала WAL:

    wal_db=# SELECT pg_current_wal_insert_lsn() AS before_insert_lsn \gset
    wal_db=# \echo :before_insert_lsn
    0/2119058

    Вспомним, что эта позиция вставки указывает на LSN записи, следующей за последней вставленной в журнал WAL.

    Следующая запись WAL будет вставлена именно на эту позицию.

  5. Вставьте одну строку в таблицу и посмотрите, как изменилась позиция вставки:

    wal_db=# INSERT INTO some_table VALUES (1, 100);
    INSERT 0 1
    wal_db=# SELECT pg_current_wal_insert_lsn() AS after_insert_lsn \gset
    wal_db=# \echo :after_insert_lsn
    0/2119208

    Позиция вставки увеличилась.

  6. Узнайте размер записей WAL между сохраненными LSN:

    wal_db=# SELECT pg_size_pretty(:'after_insert_lsn'::pg_lsn - :'before_insert_lsn'::pg_lsn);
    pg_size_pretty
    ----------------
    432 bytes
    (1 строка)

    Вычитая значения типа pg_lsn, можно получить размер записей WAL между ними.

    В данном случае были сгенерированы записи WAL общим размером 432 байта.

    При этом в указанных записях должны присутствовать записи для операции вставки и фиксации транзакции (фиксация была выполнена неявно).

    Однако в указанный интервал могли также попасть и записи для других операций.

    Вспомним, что журнал WAL общий для всего кластера баз данных.

  7. Посмотрите содержимое заголовков сгенерированных записей WAL:

    wal_db=# SELECT start_lsn, resource_manager, record_type, record_length, block_ref FROM pg_get_wal_records_info(:'before_insert_lsn', :'after_insert_lsn');
    start_lsn | resource_manager | record_type | record_length | block_ref
    -----------+------------------+-------------------+---------------+-------------------------------------------------
    0/2119058 | XLOG | CHECKPOINT_ONLINE | 148 |
    0/21190F0 | Heap | INSERT+INIT | 81 | blkref #0: rel 1663/16390/16457 fork main blk 0
    0/2119148 | Transaction | COMMIT | 36 |
    0/2119170 | XLOG | CHECKPOINT_ONLINE | 148 |
    (4 строки)

    Для просмотра заголовков записей WAL была использована функция pg_get_wal_records_info из расширения pg_walinspect.

    Для операции вставки была создана запись с типом INSERT+INIT. INIT означает, что для вставки также была создана новая табличная страница. Ссылка на нее имеется в поле block_ref. Менеджер ресурсов для данной записи — Heap. Вспомним, что менеджер ресурсов отвечает за интерпретацию записей различных типов.

    Следом за записью, соответствующей вставке, сохранилась запись о фиксации транзакции. Менеджер ресурсов для этой записи Transaction.

    Однако в журнале оказались еще две записи типа CHECKPOINT_ONLINE. Это записи о выполнении контрольных точек процессом checkpointer. Они необходимы для обеспечения возможности удаления старых уже ненужных для восстановления записей из журнала.

    Более подробно контрольные точки будут рассмотрены в следующей теме.

  8. Посмотрите, какой сейчас LSN в заголовке измененной табличной страницы:

    wal_db=# SELECT lsn FROM page_header(get_raw_page('some_table',0));
    lsn
    -----------
    0/2119148
    (1 строка)

    Вспомним, что для соблюдения порядка сброса на диск журнальных записей и измененных страниц в заголовке страницы сохраняется LSN записи, следующей за записью, соответствующей последней изменившей страницу операции.

    Действительно, страницу последней изменила операция INSERT+INIT. LSN следующей записи в журнале как раз 0/2119148.

  9. Наконец, посмотрите, в какой файл-сегмент журнала WAL была вставлена первая запись:

    wal_db=# SELECT * FROM pg_walfile_name_offset(:'before_insert_lsn');
    file_name | file_offset
    --------------------------+-------------
    000000010000000000000002 | 1151064
    (1 строка)

    Функция pg_walfile_name_offset позволяет по LSN записи определить сегмент журнала, в котором она находится, а также смещение относительно начала сегмента.

Операции в журнале на уровне replica​

  1. Сбросьте значения конфигурационных параметров wal_level и max_wal_senders:
wal_db=# ALTER SYSTEM RESET wal_level;
ALTER SYSTEM
wal_db=# ALTER SYSTEM RESET max_wal_senders;
ALTER SYSTEM

Вспомним, что параметр wal_level по умолчанию принимает значение replica.

  1. Перезапустите экземпляр Pangolin и проверьте значения сброшенных параметров:

    wal_db=# \q
    -bash-4.4$ pg_ctl restart -l logfile
    ожидание завершения работы сервера.... готово
    сервер остановлен
    ожидание запуска сервера.... готово
    сервер запущен
    -bash-4.4$ psql -d wal_db
    psql (15.5)
    Введите "help", чтобы получить справку.
    wal_db=# SELECT name, setting FROM pg_settings WHERE name IN ('wal_level', 'max_wal_senders');
    name | setting
    -----------------+---------
    max_wal_senders | 10
    wal_level | replica
    (2 строки)
  2. Начните транзакцию и сохраните в переменную psql позицию вставки журнала WAL:

    wal_db=# BEGIN;
    BEGIN
    wal_db=*# SELECT pg_current_wal_insert_lsn() AS before_insert_lsn \gset
    wal_db=*# \echo :before_insert_lsn
    0/2149A80
  3. Очистите таблицу some_table командой TRUNCATE, вставьте в нее строку и обновите ее:

    wal_db=*# TRUNCATE some_table;
    TRUNCATE TABLE
    wal_db=*# INSERT INTO some_table VALUES (1, 100);
    INSERT 0 1
    wal_db=*# UPDATE some_table SET value = 150 WHERE id = 1;
    UPDATE 1
  4. Сохраните в переменной новую позицию вставки:

    wal_db=*# SELECT pg_current_wal_insert_lsn() AS after_insert_lsn \gset
    wal_db=*# \echo :after_insert_lsn
    0/214DB28
  5. Посмотрите содержимое заголовков сгенерированных записей WAL:

    wal_db=*# SELECT start_lsn, resource_manager, record_type, record_length FROM pg_get_wal_records_info(:'before_insert_lsn', :'after_insert_lsn');
    start_lsn | resource_manager | record_type | record_length
    -----------+------------------+---------------+---------------
    0/2149A80 | Standby | RUNNING_XACTS | 68
    0/2149AC8 | Standby | LOCK | 52
    0/2149B00 | Storage | CREATE | 44
    0/2149B30 | Heap | UPDATE | 141
    0/2149BC0 | XLOG | FPI_FOR_HINT | 2571
    0/214A5E8 | Btree | INSERT_LEAF | 66
    0/214A630 | XLOG | FPI_FOR_HINT | 6719
    0/214C088 | Btree | INSERT_LEAF | 74
    0/214C0D8 | Btree | INSERT_LEAF | 6283
    0/214D968 | Standby | LOCK | 52
    0/214D9A0 | Standby | RUNNING_XACTS | 76
    0/214D9F0 | Heap | INSERT+INIT | 81
    0/214DA48 | Heap | HOT_UPDATE | 85
    0/214DAA0 | Standby | LOCK | 52
    0/214DAD8 | Standby | RUNNING_XACTS | 76
    (15 строк)

    Вспомним, что на уровне журнала replica к перечню информации уровня minimal также добавляется информация об операциях массовой вставки, исключительных блокировках и статусах транзакций.

    В данном эксперименте командой TRUNCATE была наложена исключительная блокировка на таблицу some_table.

    В результате в журнале появились записи менеджера ресурсов Standby типа LOCK для исключительной блокировки и типа RUNNING_XACTS для статусов транзакций.

    Также обратите внимание, что для операции обновления была создана запись типа HOT_UPDATE, что подтверждает применение оптимизации HOT при обновлении. Действительно, у таблицы some_table нет индексов, поэтому все внутристраничные обновления являются HOT-обновлениями.

    Записи вверху таблицы (до записи с типом INSERT+INIT) во многом соответствуют изменениям в системном каталоге, вызванным операцией TRUNCATE.

    Среди них можно выделить записи с типом INSERT_LEAF менеджера ресурсов Btree, относящиеся к созданию новых ключей в листовых узлах индекса.

  6. Откатите транзакцию:

    wal_db=*# ROLLBACK;
    ROLLBACK

Сравнение синхронного и асинхронного режимов​

  1. Посмотрите значение параметра synchronous_commit:

    wal_db=# SHOW synchronous_commit;
    synchronous_commit
    --------------------
    on
    (1 строка)

    По умолчанию журнальные записи сбрасываются в энергонезависимые накопители в синхронном режиме.

  2. Откройте второй терминал и перейдите в режим выполнения команд от имени пользователя postgres:

    [student@pkles-gt0041307 ~]$ sudo -iu postgres
  3. Во втором сеансе инициализируйте и выполните минутный тест pgbench:

    -bash-4.4$ pgbench -i wal_db
    dropping old tables...
    NOTICE: table "pgbench_accounts" does not exist, skipping
    NOTICE: table "pgbench_branches" does not exist, skipping
    NOTICE: table "pgbench_history" does not exist, skipping
    NOTICE: table "pgbench_tellers" does not exist, skipping
    creating tables...
    generating data (client-side)...
    100000 of 100000 tuples (100%) done (elapsed 0.01 s, remaining 0.00 s)
    vacuuming...
    creating primary keys...
    done in 0.23 s (drop tables 0.00 s, create tables 0.01 s, client-side generate 0.13 s, vacuum 0.04 s, primary keys 0.05 s).
    -bash-4.4$ pgbench -T 60 wal_db
    pgbench (15.5)
    starting vacuum...end.
    transaction type: <builtin: TPC-B (sort of)>
    scaling factor: 1
    query mode: simple
    number of clients: 1
    number of threads: 1
    maximum number of tries: 1
    duration: 60 s
    number of transactions actually processed: 48757
    number of failed transactions: 0 (0.000%)
    latency average = 1.231 ms
    initial connection time = 4.175 ms
    tps = 812.659751 (without initial connection time)

    При синхронной фиксации транзакций время отклика (latency average) составило 1.231 ms, а скорость выполнения транзакций tps = 812.659751.

  4. В первом сеансе выключите синхронный режим фиксации транзакций:

    wal_db=# ALTER SYSTEM SET synchronous_commit = off;
    ALTER SYSTEM
    wal_db=# SELECT pg_reload_conf();
    pg_reload_conf
    ----------------
    t
    (1 строка)
    wal_db=# SHOW synchronous_commit;
    synchronous_commit
    --------------------
    off
    (1 строка)
  5. Во втором сеансе повторите тест pgbench:

    -bash-4.4$ pgbench -T 60 wal_db
    pgbench (15.5)
    starting vacuum...end.
    transaction type: <builtin: TPC-B (sort of)>
    scaling factor: 1
    query mode: simple
    number of clients: 1
    number of threads: 1
    maximum number of tries: 1
    duration: 60 s
    number of transactions actually processed: 118888
    number of failed transactions: 0 (0.000%)
    latency average = 0.505 ms
    initial connection time = 3.580 ms
    tps = 1981.565811 (without initial connection time)

    При асинхронной фиксации транзакций время отклика (latency average) уменьшилось более чем в два раза и составило 0.505 ms, а скорость выполнения транзакций увеличилась более чем в два раза: tps = 1981.565811.

    На основании этого можно сделать вывод, что в OLTP-системах с большим количеством транзакций в единицу времени режим фиксации транзакций может существенно влиять на производительность системы.

    В то же время стоит помнить, что в асинхронном режиме в случае сбоя часть последних зафиксированных изменений может быть потеряна.

Завершение​

  1. В первом сеансе сбросьте значение параметра synchronous_commit:
wal_db=# ALTER SYSTEM RESET synchronous_commit;
ALTER SYSTEM
wal_db=# SELECT pg_reload_conf();
pg_reload_conf
----------------
t
(1 строка)
wal_db=# SHOW synchronous_commit;
synchronous_commit
--------------------
on
(1 строка)
  1. В первом сеансе подключитесь базе данных postgres, удалите базу данных wal_db и выйдите из сеанса:

    wal_db=# \c postgres
    Вы подключены к базе данных "postgres" как пользователь "postgres".
    postgres=# DROP DATABASE wal_db;
    DROP DATABASE
    postgres=# \q

Самопроверка​

Вопрос 1

Состояние журнала предзаписи за некоторый интервал LSN представлено ниже:

start_lsn | resource_manager | record_type
-----------+------------------+-------------
0/97933B8 | Heap             | INSERT+INIT
0/9793410 | Heap             | HOT_UPDATE
0/9793468 | Heap             | INSERT

В заголовке табличной страницы значение поля pd_lsn = '0/9793410'.

Журнальная запись какого типа была сформирована в результате последнего изменения указанной табличной страницы?

Вопрос 2

В каталоге pg_wal находятся следующие сегменты журнала предзаписи:

name
--------------------------
000000010000000000000009
00000001000000000000000A
00000001000000000000000B
00000001000000000000000C

В каком из сегментов находится журнальная запись с LSN = '0/9B996AA'?

Вопрос 3

Состояние журнала предзаписи за некоторый интервал LSN представлено ниже:

start_lsn | resource_manager |  record_type  | record_length
-----------+------------------+---------------+---------------
0/9799650 | Heap             | HOT_UPDATE    |            85
0/97996A8 | Standby          | RUNNING_XACTS |            76
0/97996F8 | Heap             | INSERT        |            65
0/9799740 | Standby          | RUNNING_XACTS |            76
0/9799790 | Transaction      | COMMIT        |            36

К какому уровню (wal_level) может относиться журнал предзаписи с представленным состоянием? Выберите все верные варианты ответа:

Вопрос 4

Выберите все верные утверждения относительно асинхронного режима фиксации транзакций