March 12, 2010

В ожидании 9.0 - Потоковая репликация

Перевод Waiting for 9.0 – Streaming replication с select * from depesz;

(В январе 2010 г. команда разработчиков решила, что следующая версия PostgreSQL будет нумероваться 9.0, а не 8.5)


Это очень важный функционал, возможность благодаря которой PostgreSQL сделал скачёк от 8.4 до 9.0. Патч разработан Fujii Masao и принят Heikki Linnakangas 15 января 2010.

Добавляет Потоковую репликацию (Streaming replication).

Включает два новых вида процессов postmaster-а - walsender и walreceiver. Walreceiver отвечает за соединение с главным сервером и передачу WALL данных на диск, в то время как walsender работает на главном сервере и передаёт клиенту WAL данные с диска.

Документация всё ещё нуждается в доработке, но основа уже есть. Возможно мы позже вынесем секцию касающуюся репликации в отдельную главу, также как и секцию о репликации на основе файлов. Это будет в отдельном патче, чтобы было понятно что добавлено/изменено. Этот патч также добавляет новую секцию в главу о FE/BE протоколе, касающуюся walsender/walreceivxer

Подняли версию каталога из-за двух новых функций pg_last_xlog_receive_location() и pg_last_xlog_replay_location() для мониторинга прогресса репликации.

Fujii Masao, с дополнительными правками от меня


Что же это всё значит?

Как вы возможно знаете, PostgreSQL предоставляет механизм репликации основанный на WAL сегментах, который с версии 8.4 может быть использован как Warm Standby, но с 9.0 вы сможете использовать его уже как Hot Standby.

Проблема с обычной репликацией в том, что она основана на файлах, которые ротируются по достижению 16MB. Конечно, в некоторых случаях это не проблема - например, если у вас постоянно генерируется не менее 16MB данных в минуту, то лаг (запаздывание данных) будет всегда до минуты. Но что если такой интенсивности записи нет? Конечно можно формировать ротацию каждую минуту, но это не очень хорошая идея, и более того не рекомендуется.

Сейчас же, благодаря Потоковой репликации, лаг может быть сильно приближен к нулю. Конечно она всё ещё асинхронная, но в большинстве случаев этого вполне достаточно. Давайте посмотрим как её настроить.

На моей тестовой машине бинарники установлены в /home/pgdba/work, а главная директория данных в /home/pgdba/data (на порту 5850). Теперь установим потоковую репликацию и резервный (standby) сервер на этой же машине в /home/pgdba/data2, на порту 5851. Первым делом остановим главный сервер:

/home/pgdba/work/bin/pg_ctl -D /home/pgdba/data stop

Затем сделаем кое-какие изменения в конфигурации. Перед изменением я скопировал postgresql.conf в postgresql.conf.before, т.ч. просто покажу вам diff:

=> diff -u postgresql.conf.before postgresql.conf
--- postgresql.conf.before 2010-01-31 23:27:24.000000000 +0100
+++ postgresql.conf 2010-01-31 23:29:29.000000000 +0100
@@ -175,9 +175,9 @@

# - Archiving -

-#archive_mode = off # allows archiving to be done
+archive_mode = on # allows archiving to be done
# (change requires restart)
-#archive_command = '' # command to use to archive a logfile segment
+archive_command = 'cp "%p" /home/pgdba/wal_archive/"%f"' # command to use to archive a logfile segment
#archive_timeout = 0 # force a logfile segment swich after this
# number of seconds; 0 disables

@@ -190,7 +190,7 @@

# - Replication -

-#max_wal_senders = 0 # max number of walsender processes
+max_wal_senders = 1 # max number of walsender processes
#wal_sender_delay = 200ms # 1-10000 milliseconds


Как видно тут нет ничего сложного. Теперь запускаем главный сервер:

/home/pgdba/work/bin/pg_ctl -D /home/pgdba/data start

Ок. Сгенерим какой-нибудь трафик в базе, чтобы появились wal-сегменты, не слишком много - где-то 100. Мой тестовый скрипт генерирует примерно 7 wal-сегментов в минуту.

Теперь настроим подчинённый (slave) сервер:

=> /home/pgdba/work/bin/psql -U postgres -p 5850 -c "SELECT pg_start_backup('depesz')"
pg_start_backup
-----------------
0/74C645E8
(1 row)
=> rsync -a /home/pgdba/data/ /home/pgdba/data2/
=> /home/pgdba/work/bin/psql -U postgres -p 5850 -c "SELECT pg_stop_backup()"
pg_stop_backup
----------------
0/7A8FB9A8
(1 row)


Почистим data2:

rm -f /home/pgdba/data2/postmaster.pid /home/pgdba/data2/pg_xlog/archive_status/* /home/pgdba/data2/pg_xlog/0*

И поменяем конфигурацию. Diff между data и data2:

=> diff -u data/postgresql.conf data2/postgresql.conf
--- data/postgresql.conf 2010-01-31 23:34:11.000000000 +0100
+++ data2/postgresql.conf 2010-02-01 00:02:18.000000000 +0100
@@ -60,7 +60,7 @@
# comma-separated list of addresses;
# defaults to 'localhost', '*' = all
# (change requires restart)
-#port = 5850 # (change requires restart)
+port = 5851 # (change requires restart)
max_connections = 100 # (change requires restart)
# Note: Increasing max_connections costs ~400 bytes of shared memory per
# connection slot, plus lock space (see max_locks_per_transaction).
@@ -175,7 +175,7 @@

# - Archiving -

-archive_mode = on # allows archiving to be done
+archive_mode = off # allows archiving to be done
# (change requires restart)
archive_command = 'cp "%p" /home/pgdba/wal_archive/"%f"' # command to use to archive a logfile segment
#archive_timeout = 0 # force a logfile segment swich after this
@@ -190,7 +190,7 @@

# - Replication -

-max_wal_senders = 1 # max number of walsender processes
+max_wal_senders = 0 # max number of walsender processes
#wal_sender_delay = 200ms # 1-10000 milliseconds


На подчинённом сервере создаём recovery.conf:

=> cat /home/pgdba/data2/recovery.conf
standby_mode = 'on'
primary_conninfo = 'host=127.0.0.1 port=5850 user=postgres'
trigger_file = '/home/pgdba/data2/finish.replication'
restore_command = 'cp -i /home/pgdba/wal_archive/%f "%p" < /dev/null'


Важно что мы не можем использовать здесь pg_standby или что-то другое, что будет ждать следующего wal-сегмента. Также, насколько я понял, wal-сегменты должны быть отправлены с главного сервера подчинённому с помощью archive_command, т.е. копированние их во временную директорию и rsync из cron-а скорее всего не сработает.

И так, спустя какое-то время подчинённый сервер забрал все wal-сегменты из архивной директории и переключился в потоковый режим.

Это можно понять по 2-м новым интересным процессам:

=> ps uw --ppid $( head -n 1 /home/pgdba/data2/postmaster.pid )
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
pgdba 13384 0.0 0.0 28712 1056 ? Ss 00:22 0:00 postgres: logger process
pgdba 13385 1.1 0.2 59916 27200 ? Ss 00:22 0:03 postgres: startup process recovering 0000000100000000000000EE
pgdba 13390 0.0 0.2 59812 26208 ? Ss 00:22 0:00 postgres: writer process
pgdba 13397 0.0 0.0 30800 1052 ? Ss 00:22 0:00 postgres: stats collector process
pgdba 13402 0.4 0.0 77016 7368 ? Ss 00:22 0:01 postgres: wal receiver process streaming 0/EE2B7FD8

=> ps uw --ppid $( head -n 1 /home/pgdba/data/postmaster.pid )
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
pgdba 6904 0.9 0.0 28716 1056 ? Ss Jan31 0:31 postgres: logger process
pgdba 6906 0.1 0.2 60092 27060 ? Ss Jan31 0:04 postgres: writer process
pgdba 6907 0.0 0.0 59652 1304 ? Ss Jan31 0:00 postgres: wal writer process
pgdba 6908 0.0 0.0 62624 3992 ? Ss Jan31 0:00 postgres: autovacuum launcher process
pgdba 6909 0.0 0.0 30912 1024 ? Ss Jan31 0:00 postgres: archiver process last was 0000000100000000000000ED
pgdba 6910 0.0 0.1 47356 12896 ? Ss Jan31 0:00 postgres: stats collector process
pgdba 8713 85.6 0.6 118308 80628 ? Rs Jan31 31:35 postgres: depesz depesz [local] DROP TABLE
pgdba 8818 0.0 0.5 110428 66604 ? Ss Jan31 0:01 postgres: autovacuum worker process depesz
pgdba 8887 0.0 0.6 134780 84884 ? Ss Jan31 0:01 postgres: autovacuum worker process depesz
pgdba 8952 0.0 0.6 126812 83580 ? Ss Jan31 0:01 postgres: autovacuum worker process depesz
pgdba 13403 1.4 0.0 69224 8580 ? Ss 00:22 0:04 postgres: wal sender process postgres 127.0.0.1(52186) streaming 0/EE3D8090


Как можно заметить, главный сервер ещё отправляет wal-сегменты в архив. Это так и будет продолжаться, т.ч. надо будет добавить что-то в cron, что будет удалять не нужные. Под не нужными я подразумеваю те, которые старше последнего REDO чекпоинта из pg_controldata SLAVE_DATA_DIR/.

Теперь посмотрим как действительно это всё работает.

Остановим наш генератор нагрузки, после чего трафик на базе должен полностью исчезнуть, конечно же придётся дождаться отработки autovacuum :)

Далее проведём простое тестирование. Есть таблица:

# \d test
Table "public.test"
Column | Type | Modifiers
--------+--------------------------+-----------
x | timestamp with time zone | not null
Indexes:
"test_pkey" PRIMARY KEY, btree (x)


Тест первый:

=$ /home/pgdba/work/bin/psql -U depesz -p 5850 -c "insert into test (x) values (now()) returning x"
x
-------------------------------
2010-02-01 00:33:29.543639+01
(1 row)

INSERT 0 1

=$ /home/pgdba/work/bin/psql -U depesz -p 5851 -c "select * from test"
x
---
(0 rows)


Ничего нет! Потоковая репликация конечно быстрая, но не настолько. Добавим задержку в 1 секунду и всё будет замечательно:

=$ /home/pgdba/work/bin/psql -U depesz -p 5850 -c "insert into test (x) values (now()) returning x"
x
-------------------------------
2010-02-01 00:35:13.520617+01
(1 row)

INSERT 0 1

=$ sleep 1

=$ /home/pgdba/work/bin/psql -U depesz -p 5851 -c "select * from test order by x desc limit 1"
x
-------------------------------
2010-02-01 00:35:13.520617+01
(1 row)


Как хорошо видно всё работает.

Спросите всё ли уже завершено? Определённо нет. Для начала - документация далека от совершенства. Если захотите сами это попробовать, можете посмотреть doc, но Wiki page открыть будет просто необходимо.

Понравилось ли это мне? О, ДА! Даже с не законченной документацией настройка заняла не много времени, а эффект достоин всяческих похвал :)

No comments:

Post a Comment