PGSQL 06: Репликация

Описание:

В предшествующих заданиях вы познакомились с основными возможностями СУБД 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 отыщет неполную запись журнала, поскольку не будет совпадать контрольная сумма. Эта запись будет отброшена, а транзакции, которые помещали в нее данные, будут откачены.
  • Если система «упала» во время записи в файлы данных, но журнал транзакций не поврежден, то PostgreSQL просмотрит весь журнал транзакций, проверит, что его содержимое помещено в файлы данных, и при необходимости исправит файлы данных. Нет нужды просматривать их все, поскольку из журнала транзакций известно, какую часть какого файла предполагалось изменить и как именно.

Процесс воспроизведения журнала транзакций называется восстановлением. Если имеется полный журнал транзакций от момента инициализации сервера до текущего момента, то можно восстановить состояние базы на любой момент времени в прошлом. Чтобы обеспечить такую функциональность, можно сконфигурировать PostgreSQL так, чтобы старые WAL-файлы не удалялись, а где-то архивировались. Тогда архив можно будет использовать для восстановления базы данных на момент времени на другой машине.

Программные реализации репликации в PostgreSQL

Для репликации PostgreSQL существует несколько решений, как закрытых, так и свободных.

Cписок свободных решений:

  • Slony-I — асинхронная Master-Slave репликация, поддерживает каскады (cascading) и отказоустойчивость(failover). Slony-I использует триггеры PostgreSQL для привязки к событиям INSERT/DELETE/UPDATE и хранимые процедуры для выполнения действий;

  • Pgpool-I/II — это замечательный инструмент для PostgreSQL (лучше сразу работать с II версией). Позволяет делать:

    1. репликацию (в том числе, с автоматическим переключением на резервный stand-by сервер);
    2. online-бэкап;
    3. pooling коннектов;
    4. очередь соединений;
    5. балансировку SELECT-запросов на несколько postgresql серверов;
    6. разбиение запросов для параллельного выполнения над большими объемами данных;
  • 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).

Чтобы настроить трансляцию журналов, нужно выполнить следующие действия:

  • на ведущем сервере:

    • гарантировать, что в WAL-файлах достаточно информации для репликации: в файле postgresql.conf установить параметр wal_level равным replica или logical;
    • включить архивацию WAL-файлов, присвоив параметру archive_mode значение on;
    • заставить PostgreSQL архивировать WAL-файлы в безопасное место, задав конфигурационный параметр archive_command. Сервер будет применять эту команду ОС к каждому WAL-файлу, нуждающемуся в архивации. Команда может, например, сжимать файл и копировать его на сетевой диск. Если команда не задана, но режим архивации WAL-файлов включен, то эти файлы будут накапливаться в каталоге pg_wal;
  • на ведомом сервере:

    • восстановить базу из резервной копии, снятой на ведущем сервере. Проще всего сделать это с помощью программы pg_basebackup. Вот, например, как может выглядеть команда, выполняемая на ведомом сервере:
    postgres@standby:~$ pg_basebackup -D /var/lib/postgresql/10/main -h master -U postgres
    

    Подробней об утилите pg_basebackup мы поговорим позже.

    • добавьте в файл postgresql.conf описание, как сервер должен выполнять восстановление:
    restore_command = 'cp /wal_archive_location/%f %p'
    

Значение параметра restore_command зависит от положения архива WAL-файлов. Это команда ОС, которая должна скопировать WAL-файл из архива туда, где находится журнал транзакций.

После того как все будет настроено и оба сервера начнут работать, ведущий сервер будет копировать все WAL-файлы в архивный каталог, а ведомый сервер будет брать их оттуда и воспроизводить в своей базе.

Если ведущий сервер «упадет» и нужно будет переключиться на ведомый, его следует «повысить в чине». Это означает, что он должен прекратить восстановление и разрешить транзакции чтения-записи. Для этого достаточно просто удалить файл recovery.signal/standby.signal и postgresql.auto.conf, затем перезапустить сервер. В результате ведомый сервер становится новым ведущим. После восстановления старого ведущего сервера имеет смысл сделать его ведомым, чтобы сохранить резервирование кластера. В таком случае нужно повторить описанную выше последовательность действий, поменяв серверы ролями. Ни в коем случае не запускайте сервер как ведущий, если ведомый сервер был повышен. Это может привести к различиям в репликах и, в конечном итоге, к потере данных!

Перечислим преимущества репликации методом трансляции журналов:

  • ее сравнительно легко настроить;
  • нет необходимости устанавливать соединение между ведущим и ведомым серверами;
  • ведущий сервер не знает о существовании ведомого и не зависит от него;
  • ведомых серверов может быть несколько. На самом деле ведомый сервер может выступать в роли ведущего для других серверов. Это называется каскадной репликацией.

С другой стороны, есть и проблемы:

  • необходимо для архива WAL-файлов предоставить сетевой каталог, к которому имели бы доступ ведущий и ведомый серверы. Это значит, что нужно вовлекать в процесс третью сторону. Можно, конечно, архивировать WAL-файлы прямо на ведомом сервере, но тогда нарушилась бы симметрия, и в случае выхода из строя ведущего сервера и повышения ведомого, настройка нового ведомого стала бы несколько сложнее;
  • ведомый сервер может воспроизвести WAL-файл только после его архивации. Это произойдет лишь после того, как файл будет закрыт, то есть достигнет размера 16 МБ. А значит, последние транзакции, выполненные на ведущем сервере, могут не найти отражения на ведомом, а если транзакции на ведущем сервере небольшие и происходят не слишком часто, то резервная база данных может далеко отстать от основной. Поэтому в случае сбоя часть данных может быть потеряна.

Потоковая репликация (Streaming Replication)

Потоковая репликация (Streaming Replication, SR) работает поверх трансляции журналов или вообще без трансляции журналов, тем самым дает возможность непрерывно отправлять и применять WAL (Write-Ahead Log) записи на резервные сервера для создания точной копии текущего. Этот тип репликации простой, надежный и, вероятней всего, будет использоваться в качестве стандартной репликации в большинстве высоконагруженных приложений, что используют PostgreSQL.

Отличительные особенности:

  • репликация всего инстанса PostgreSQL;
  • асинхронный или синхронный механизмы репликации;
  • простота установки;
  • мастер-база данных может обслуживать огромное количество слейвов из-за минимальной нагрузки.

Недостатки:

  • невозможность реплицировать только определенную базу данных из всех на PostgreSQL инстансе.

Основные параметры настройки

Обозначим мастер-сервер как masterdb (192.168.1.227) и слейв как slavedb (192.168.1.222).

Предварительная настройка

Отредактируем pg_hba.conf на мастере и слейве, разрешив им доступ друг к другу без пароля (тут добавляется роль replication ):

В файле 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                                #   эта опция будет активна на ведомом сервере

Создаем лог-файл и даем права на него пользователю postgres на masterdb и slavedb.

$ 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):

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/

где

  • master_db_datadir — директория с postgresql данными на masterdb;
  • slave_db_datadir — директория с postgresql данными на slavedb;
  • slavedb_host — хост slavedb.

Пример:

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

Полезные ссылки:

Правила выполнения задания:

  1. Время создания окружения занимает до 5 минут.
  2. После нажатия кнопки «Начать выполнение» для вас будет создано окружение для выполнения задания, состоящее из одной или более виртуальнах машин с CentOS 7. К ним будут выданы IP, логин и пароль для доступа по SSH, а также при необходимости — дополнительных ресурсов.
  3. Также, если необходимо, вам могут быть выданы переменные, которые в задании указаны в фигурных скобках, — их надо будет подставить при выполнении задания (например, ${base_domain}).
  4. После выполнения всех пунктов задания нажмите кнопку «Проверить выполнение», и в течение 3-5 минут скрипт проверит выполнение всех условий и выставит вам оценку.
  5. В случае, если вы что-то забыли, можно исправить ошибку и отправить на проверку повторно (нажав кнопку «Проверить выполнение»).
  6. После получения удовлетворительной оценки нажмите кнопку «Завершить задание», чтобы созданное окружение уничтожилось и вы могли приступить к следующему заданию.
  7. Если у вас закончилось время (истек таймер) — окружение будет автоматически уничтожено и вам придется начать выполнение заново.
  8. Также, если вы успешно сдали задание, но у вас остались вопросы — вы всегда сможете задать их куратору после проверки (используя кнопку «Задать вопрос куратору») или в чате практикума в любое удобное для вас время. Обращаем внимание — кураторы проверяют вопросы в течение 24 часов.

Задание:

  1. На обоих серверах установить postgresql 13 через пакетный менеджер.
  2. Настроить потоковую репликацию между master и slave в синхронном режиме:
  • Задать hostname серверов master и slave соответственно;
  • Разрешить безусловную аутентификацию клиентов в файле pg_hba.conf для пользователя репликации (postgres) с подключением для любых адресов (0.0.0.0/0);
  • В файле postgresql.conf разрешить все имеющиеся IP-интерфейсы;
  • Вынести логи (master, slave) в файл postgresql.log по пути /var/log/;
  • Сконфигурировать на slave postgresql.auto.conf с указанием мастер ноды (с этим Вам поможет pg_basebackup);
  • Перезапустить postgres.
  1. Удостоверится в работе реплики.
  2. На мастере восстановить schema.sql и data.sql из каталога /opt.
  3. Убедиться, что схема бд и данные перенеслись на slave.
  4. Если уверены, что все сделали правильно, отправляйте задание на проверку.

Связаться с нами