В предшествующих заданиях вы познакомились с основными возможностями СУБД PostgreSQL, создали свою базу данных, разобрались с функциями, поработали с ролями и научились расширять функционал PostgreSQL с помощью модулей.
Но что делать, если нагрузка на сервер базы данных растет и один сервер уже не справляется с поступающими запросами?
В этих случаях, как правило, обращаются к основам масштабирования систем.
Этим мы и займемся! В частности, узнаем, что такое репликация, для чего она нужна и как она работает в PostgreSQL.
Репликация (от англ. replication) — механизм синхронизации содержимого нескольких копий объекта (например, содержимого базы данных).
Но если обратиться к полному определению, то вы узнаете, что репликация — это одна из техник масштабирования баз данных, применяемая для реализации процесса создания копии файлов базы данных на другие физические сервера, между которыми может осуществляться обмен обновляемыми данными или объектами. Такие копии файлов базы данных называются репликами, а процесс обмена данными — синхронизацией.
В процессе репликации изменения, сделанные в одной копии объекта, как правило, распространяются на другие копии.
Существует два вида репликации относительно синхронизации данных:
синхронная;
асинхронная.
В случае синхронной репликации, если одна реплика обновляется, все другие реплики того же фрагмента данных также должны быть обновлены в этой же транзакции. Логически это обозначает, что существует лишь одна версия данных. Но синхронная репликация имеет тот недостаток, что она создает дополнительную нагрузку при выполнении всех транзакций, в которых обновляются реплики (кроме того, могут возникать проблемы, связанные с доступностью данных).
В случае асинхронной репликации обновление одной реплики распространяется на другие спустя некоторое время, а не в той же транзакции.
Таким образом, при асинхронной репликации вводится задержка или время ожидания, в течение которого отдельные реплики могут быть фактически неидентичными (то есть определение реплики оказывается не совсем подходящим, поскольку мы не имеем дело с точными и своевременно созданными копиями). Преимущество асинхронной репликации состоит в том, что дополнительные издержки репликации не связаны с транзакциями обновлений, которые могут иметь важное значение для функционирования всего предприятия и предъявлять высокие требования к производительности. К недостаткам этой схемы относится то, что данные могут оказаться несовместимыми (с точки зрения пользователя). Иными словами, избыточность может проявляться на логическом уровне, а это, строго говоря, означает, что термин «контролируемая избыточность» в таком случае не применим.
Прежде чем переходить к деталям настройки репликации, скажем несколько слов о том, как PostgreSQL обрабатывает изменение данных на низком уровне.
В ходе обработки команды, изменяющей данные в базе, PostgreSQL записывает новые данные на диск, чтобы они сохранились в случае отказа. Данные записываются в два места.
В Linux файлы данных по умолчанию находятся в каталоге /var/lib/postgresql/number/main/base. Здесь хранятся таблицы, индексы, временные таблицы и прочие объекты. Размер этого каталога ограничен только размером диска.
Журнал транзакций по умолчанию находится в каталоге /var/lib/postgresql/number/main/pg_wal. В него записывается информация о последних изменениях, внесенных в файлы данных. Его размер задается в конфигурационном файле и по умолчанию составляет приблизительно 1 ГБ.
В оба места записываются одни и те же данные. У такой кажущейся избыточности есть основательная причина.
Представьте, что некоторая транзакция вставляет в большую таблицу текстовое значение test и случилось так, что в середине транзакции сервер «упал». Буквы te он успел записать, а остальное на диск не попало. Когда база данных «поднимется», она не сможет сказать, что запись повреждена, потому что не знает, что должно быть в поле: две буквы te или слово test. Ну ладно, эту проблему можно решить с помощью контрольных сумм. Но как база данных будет искать поврежденную запись? Проверять все контрольные суммы после неожиданного перезапуска станет очень дорого.
Но решение есть: перед тем как производить запись в файлы данных, PostgreSQL всегда пишет в журнал транзакций. Журнал транзакций (его также называют журналом предзаписи) – это список изменений, внесенных PostgreSQL в файлы данных. Он представлен набором файлов размером 16 МБ каждый (WAL-файлов), находящихся в подкаталоге pg_wal каталога, где размещена база данных. Каждый файл содержит записи, сообщающие, какой файл данных и каким образом нужно изменить. И лишь после того, как журнал транзакций сохранен на диске, производится запись в файлы данных. Когда журнал транзакций заполнится, PostgreSQL удаляет самый старый сегмент, чтобы освободить место на диске. Журнал транзакций относительно невелик, так что сервер может просмотреть его в случае неожиданной остановки.
Вот что происходит в процессе перезапуска базы данных:
Процесс воспроизведения журнала транзакций называется восстановлением. Если имеется полный журнал транзакций от момента инициализации сервера до текущего момента, то можно восстановить состояние базы на любой момент времени в прошлом. Чтобы обеспечить такую функциональность, можно сконфигурировать PostgreSQL так, чтобы старые WAL-файлы не удалялись, а где-то архивировались. Тогда архив можно будет использовать для восстановления базы данных на момент времени на другой машине.
Для репликации PostgreSQL существует несколько решений, как закрытых, так и свободных.
Cписок свободных решений:
Slony-I — асинхронная Master-Slave репликация, поддерживает каскады (cascading) и отказоустойчивость(failover). Slony-I использует триггеры PostgreSQL для привязки к событиям INSERT/DELETE/UPDATE и хранимые процедуры для выполнения действий;
Pgpool-I/II — это замечательный инструмент для PostgreSQL (лучше сразу работать с II версией). Позволяет делать:
Bucardo — асинхронная репликация, которая поддерживает MultiMaster и Master-Slave режимы, а также несколько видов синхронизации и обработки конфликтов;
Londiste — асинхронная Master-Slave репликация. Входит в состав Skytools. Проще в использовании, чем Slony-I;
Mammoth Replicator — асинхронная Multi-Master репликация;
BDR (Bi-Directional Replication) — асинхронная Multi-Master репликация;
Pglogical — асинхронная Master-Slave репликация.
Это, конечно, не весь список свободных систем для репликации, но даже из этого есть что выбрать для PostgreSQL.
Записи журнала транзакций можно взять с одного сервера базы данных – ведущего – и применить к файлам данных на другом сервере – ведомом. В таком случае ведомый сервер будет иметь точную копию базы данных на ведущем сервере. Процесс передачи записей из журнала транзакций на другой сервер и их применения называется физической репликацией. Слово «физическая» означает, что журнал транзакций работает на низком уровне и реплика базы данных на ведомом сервере будет точной побайтовой копией базы данных на ведущем сервере.
Физическая репликация работает для всех баз данных в кластере. Создание новой базы отражается в журнале транзакций и потому реплицируется на ведомый сервер.
Один из способов физической репликации – постоянно передавать новые WAL-файлы с ведущего сервера на ведомый и применять их там для получения синхронизированной копии базы данных. Это называется трансляцией журналов (log shipping).
Чтобы настроить трансляцию журналов, нужно выполнить следующие действия:
на ведущем сервере:
на ведомом сервере:
postgres@standby:~$ pg_basebackup -D /var/lib/postgresql/10/main -h master -U postgres
Подробней об утилите pg_basebackup мы поговорим позже.
restore_command = 'cp /wal_archive_location/%f %p'
Значение параметра restore_command зависит от положения архива WAL-файлов. Это команда ОС, которая должна скопировать WAL-файл из архива туда, где находится журнал транзакций.
После того как все будет настроено и оба сервера начнут работать, ведущий сервер будет копировать все WAL-файлы в архивный каталог, а ведомый сервер будет брать их оттуда и воспроизводить в своей базе.
Если ведущий сервер «упадет» и нужно будет переключиться на ведомый, его следует «повысить в чине». Это означает, что он должен прекратить восстановление и разрешить транзакции чтения-записи. Для этого достаточно просто удалить файл recovery.signal/standby.signal и postgresql.auto.conf, затем перезапустить сервер. В результате ведомый сервер становится новым ведущим. После восстановления старого ведущего сервера имеет смысл сделать его ведомым, чтобы сохранить резервирование кластера. В таком случае нужно повторить описанную выше последовательность действий, поменяв серверы ролями. Ни в коем случае не запускайте сервер как ведущий, если ведомый сервер был повышен. Это может привести к различиям в репликах и, в конечном итоге, к потере данных!
Перечислим преимущества репликации методом трансляции журналов:
С другой стороны, есть и проблемы:
Потоковая репликация (Streaming Replication, SR) работает поверх трансляции журналов или вообще без трансляции журналов, тем самым дает возможность непрерывно отправлять и применять WAL (Write-Ahead Log) записи на резервные сервера для создания точной копии текущего. Этот тип репликации простой, надежный и, вероятней всего, будет использоваться в качестве стандартной репликации в большинстве высоконагруженных приложений, что используют PostgreSQL.
Обозначим мастер-сервер как masterdb (192.168.1.227) и слейв как slavedb (192.168.1.222).
В файле pg_hba.conf необходимо разрешить пользователю, под которым пойдет репликация, подключение к виртуальной базе данных replication:
Мастер
$ nano /var/lib/pgsql/13/data/pg_hba.conf
# TYPE DATABASE USER ADDRESS METHOD
# "local" is for Unix domain socket connections only
local all all peer
# IPv4 local connections:
host all all 127.0.0.1/32 scram-sha-256
host all task012 127.0.0.1/32 md5
# IPv6 local connections:
host all all ::1/128 scram-sha-256
# Allow replication connections from localhost, by a user with the
# replication privilege.
local replication all peer
host replication all 127.0.0.1/32 scram-sha-256
host replication all ::1/128 scram-sha-256
### slavedb
host replication postgres 192.168.1.227/32 trust
Слейв
# TYPE DATABASE USER ADDRESS METHOD
# "local" is for Unix domain socket connections only
local all all peer
# IPv4 local connections:
host all all 127.0.0.1/32 scram-sha-256
host all task012 127.0.0.1/32 md5
# IPv6 local connections:
host all all ::1/128 scram-sha-256
# Allow replication connections from localhost, by a user with the
# replication privilege.
local replication all peer
host replication all 127.0.0.1/32 scram-sha-256
host replication all ::1/128 scram-sha-256
### masterdb
host replication postgres 192.168.1.222/32 trust
Либо создать отдельную роль для репликации
postgres=# CREATE USER streamer REPLICATION;
CREATE USER
И аналогично в файле pg_hba.conf необходимо разрешить этому пользователю подключение к виртуальной базе данных replication, например:
host replication streamer 192.168.1.227/32 trust
Для начала настроим masterdb. Установим параметры в postgresql.conf (/var/lib/pgsql/13/data/postgresql.conf)
Для репликации:
#------------------------------------------------------------------------------
# REPLICATION
#------------------------------------------------------------------------------
# - Master Server -
listen_addresses = '*' # - сервер начинает принимать соединения с любых хостов
#log_destination = 'stderr' # - выводить логи будем в /var/log/postgresql.log
logging_collector = on
log_directory = '/var/log'
log_filename = 'postgresql.log'
log_rotation_size = 100MB
client_min_messages = notice
log_min_messages = warning
log_min_error_statement = error
log_checkpoints = on
log_connections = on
log_disconnections = on
log_hostname = on
log_line_prefix = '%t'
wal_level = replica # сервер начнет писать в WAL логи так же,
# как и при режиме archive, добавляя информацию, необходимую
# для восстановления транзакции (можно также поставить archive , но
# тогда сервер не может быть слейвом при необходимости);
wal_log_hints = on # нужно для утилиты pg_rewind
max_wal_senders = 2 # максимальное количество слейвов;
max_replication_slots = 1 # задает максимальное число слотов репликации. По умолчанию 10
wal_keep_size = 100 # минимальный размер файлов c WAL
# сегментами в pg_xlog директории;
archive_mode = on # позволяем сохранять WAL-сегменты в указанное
# переменной archive_command хранилище.
hot_standby = on # эта опция будет активна на ведомом сервере
$ touch /var/log/postgresql.log
$ chmod a-r,u+r /var/log/postgresql.log
$ chown postgres:postgres /var/log/postgresql.log
По умолчанию репликация асинхронная
После изменения параметров перегружаем PostgreSQL на masterdb.
systemctl restart postgresql-13
Для начала нам потребуется создать на slavedb точную копию masterdb .
В последних версиях postgres перенос осуществляется с помощью pg_basebackup.
pg_basebackup -h master_db_ip -U replication_user -p 5432 -D $PGDATA -Fp -Xs -P -R
Пример:
pg_basebackup -h 192.168.88.177 -U postgres -p 5432 -D /var/lib/pgsql/13/backups -Fp -Xs -P -R
На всякий случай сделать резервную копию текущего каталога data на слейве:
cp -r data data_orig
Затем восстановить каталог data из backups:
cp -r backups data
Но есть важный файл standby.signal, который должен существовать в резервном каталоге данных, чтобы postgres определил его состояние как резервное. Он создается автоматически, когда вы применяете опцию -R при использовании pg_basebackup. Если нет, вы можете просто создать пустой файл вручную (touch standby.signal). Второй по значимости файл postgresql.auto.conf — это файл конфигурации, который читается в конце при запуске Postgres. При использовании pg_basebackup он формируется автоматически. Иначе добавить в него строку:
primary_conninfo = 'user=replication_user passfile=''/var/lib/pgsql/.pgpass'' channel_binding=prefer host=master_db_ip port=5432 sslmode=prefer sslcompression=0 ssl_min_protocol_version=TLSv1.2 gssencmode=prefer krbsrvname=postgres target_session_attrs=any'
Начальное копирование, или создание базовой резервной копии, раньше выполнялось при помощи утилит rsync, либо scp, для чего может потребоваться прямая безопасная авторизация, например с использованием SSH и обмена ключами. В качестве демонстрационного примера, рассмотрим подготовительные действия.
Для начала позволим определенному пользователю без пароля ходить по ssh. Пусть это будет postgres юзер. Если-же нет, то создаем набором команд:
sudo groupadd rebrainssh
$ sudo useradd -m - g rebrainssh - d /home/rebrainssh - s /bin/bash \
- c "rebrainssh allow " rebrainssh
Дальше выполняем команды от имени пользователя (в данном случае postgres):
$ su postgres
Генерим RSA-ключ для обеспечения аутентификации в условиях отсутствия возможности использовать пароль:
$ ssh-keygen -t rsa -P ""
Generating public/private rsa key pair.
Enter file in which to save the key (/var/lib/pgsql/.ssh/id_rsa):
Created directory '/var/lib/pgsql/.ssh'.
Your identification has been saved in /var/lib/pgsql/.ssh/id_rsa.
Your public key has been saved in /var/lib/pgsql/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:E2l5s4PicX8nf0Dv08bhP4UVbdKVmx/Q+bhmfeNMMuA postgres@localhost.localdomain
The key's randomart image is:
+---[RSA 2048]----+
| ..*|
| o ..=+|
| = o .+=|
| . + + o+o|
| o S + .. *o|
| . + o E o*+*|
| . . ooO+*|
| . + **|
| .o=|
+----[SHA256]-----+
И добавляем его в список авторизованных ключей:
$ cat $HOME/.ssh/id_rsa.pub >> $HOME/.ssh/authorized_keys
Проверить работоспособность соединения можно, просто написав:
ssh localhost
The authenticity of host 'localhost (::1)' can't be established.
ECDSA key fingerprint is SHA256:rgtz1Qucg7UGr/NyribYU8CNR93FY1QzuGvGo8b243s.
ECDSA key fingerprint is MD5:35:ff:c9:7b:13:53:a6:40:40:66:c8:29:df:a1:77:d7.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'localhost' (ECDSA) to the list of known hosts.
После успешно проделанной операции скопируйте $HOME/.ssh на slavedb. Теперь мы должны иметь возможность без пароля заходить с мастера на слейв и со слейва на мастер через ssh.
ЕСЛИ под пользователем postgres возникает проблема с подключением по ssh в семействе Red Hat с включенной функцией Security Enchanced Linux (SELinux), то нужно выполнить следующее:
& su postgres
& restorecon -Rv ~/.ssh
После чего поработаем с masterdb сервер и выполним под пользователем postgres в консоли:
psql -c "SELECT pg_start_backup ('label', true)"
Теперь нам нужно перенести данные с мастера на слейв. Выполняем на мастере:
rsync -C -a --delete -e ssh --exclude pg_hba.conf --exclude postmaster.pid \
--exclude postmaster.opts --exclude pg_log --exclude pg_xlog \
master_db_datadir /slavedb_host:slave_db_datadir/
где
Пример:
rsync -C -a --delete -e ssh --exclude pg_hba.conf --exclude postmaster.pid \
--exclude postmaster.opts --exclude pg_log --exclude pg_xlog \
/var/lib/pgsql/13/data/ 192.168.1.222:/var/lib/pgsql/13/data/
ВАЖНО: rsync должен быть установлен как на мастере, так и на слейве
После копирования данных с мастера на слейв остановим онлайн-бэкап. Выполняем на мастере:
psql -c "SELECT pg_stop_backup( )"
ВАЖНО: с обоих хостов должен быть доступен порт 5432
cat ~/13/data/postgresql.conf | grep listen_addresses
listen_addresses = '*' # what IP address(es) to listen on;
После изменения параметров перегружаем PostgreSQL на slavedb.
systemctl restart postgresql-13
В результате можем посмотреть отставание слейва от мастера с помощью таких команд: С masterdb
psql -c "SELECT * from pg_stat_replication";
psql -c "SELECT * from pg_stat_database_conflicts";
$ psql
CREATE DATABASE task06;
\c task06;
create table test (id int not null primary key, name varchar (20));
insert into test(id, name) values('1', 'test1');
$ psql task06
task06=# select * from test;
id | name
----+-------
1 | test1
(1 строка)
Как видим, таблица с данными успешно скопирована с мастера на слейв. Более подробно по настройке данной репликации можно почитать в официальной wiki.
Не забываем разрешить порт postgres 5432 с мастера, чтобы репликация прошла
sudo firewall-cmd --permanent --zone=trusted --add-source=192.168.88.176/32
sudo firewall-cmd --permanent --zone=trusted --add-port=5432/tcp
sudo firewall-cmd --reload
${base_domain}
).pg_hba.conf
для пользователя репликации (postgres) с подключением для любых адресов (0.0.0.0/0
);postgresql.log
по пути /var/log/
;schema.sql
и data.sql
из каталога /opt
.Чтобы начать выполнение задания, укажите в настройках вашу должность, название компании и аккаунт в Telegram
Заполнить