9.7 KiB
9.7 KiB
1. Настраиваем асинхронную репликацию.
Я решил сделать сразу три хоста - один мастер и два слейва, чтобы к этому не возвращаться в 5-м пункте.
[mysqld]
skip-host-cache
skip-name-resolve
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_do_db = app
[mysqld]
skip-host-cache
skip-name-resolve
server-id = 2
log_bin = /var/log/mysql/mysql-bin.log
relay-log = /var/log/mysql/mysql-relay-bin.log
binlog_do_db = app
[mysqld]
skip-host-cache
skip-name-resolve
server-id = 3
log_bin = /var/log/mysql/mysql-bin.log
relay-log = /var/log/mysql/mysql-relay-bin.log
binlog_do_db = app
-
Инициализация кластера:
- на мастере создаем базу, пользователя для работы приложения и пользователя для репликации:
CREATE DATABASE IF NOT EXISTS app CHARACTER SET utf8 COLLATE utf8_general_ci; GRANT ALL ON app.* TO "app"@"%" IDENTIFIED BY "app"; GRANT REPLICATION SLAVE ON *.* TO "mydb_slave_user"@"%" IDENTIFIED BY "mydb_slave_pwd"; FLUSH PRIVILEGES;
- на обоих слэйвах создаем базу и пользователя для работы приложения:
CREATE DATABASE IF NOT EXISTS app CHARACTER SET utf8 COLLATE utf8_general_ci; GRANT ALL ON app.* TO "app"@"%" IDENTIFIED BY "app"; FLUSH PRIVILEGES;
- определяем текущий IP адрес мастера:
docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "mysql_master"
- определяем текущий лог файл мастера и позицию в нем:
mysql -u root -e "SHOW MASTER STATUS"' | grep mysq | awk '{print $1}' mysql -u root -e "SHOW MASTER STATUS"' | grep mysq | awk '{print $2}'
- на обоих слейвах назначаем мастера и запускаем репликацию:
CHANGE MASTER TO MASTER_HOST={IP_мастера},MASTER_USER='mydb_slave_user',MASTER_PASSWORD='mydb_slave_pwd',MASTER_LOG_FILE={текущий_лог},MASTER_LOG_POS={текущая_позиция}; START SLAVE;
- на мастере создаем базу, пользователя для работы приложения и пользователя для репликации:
2. Выбираем 2 любых запроса на чтения (в идеале самых частых и тяжелых по логике работы сайта) и переносим их на чтение со слейва.
Я решил поэкспериментировать с теми же поисковыми запросами, над которыми мы экспериментировали в ДЗ002. Тем более, что все инструменты для этого уже есть в наличии (wrk).
-
Добавил в структуру конфига адреса мастера и двух слейвов:
type DSN struct { Master string Slave1 string Slave2 string Port string User string Pass string Base string }
-
В хэндлере страницы app:port/search создал селектор, который направляет запросы чтения на первый слейв, если он присутствует в конфигурации:
db := app.DBMaster if app.Config.DSN.Slave1!="" { db = app.DBSlave1 }
3. Делаем нагрузочный тест по странице, которую перевели на слейв до и после репликации. Замеряем нагрузку мастера (CPU, la, disc usage, memory usage).
-
Разворачиваем prom&grafana в docker настраиваем dashboard grafana на docker контейнеры
sudo make prom-up
-
Запускаем приложение с подключением только к мастеру и с помощью wrk и lua скрипта нагружаем страницу app:port/search:
sudo make app-up sudo docker run --rm -v /root/scripts:/scripts williamyeh/wrk -t1 -c10 -d5m --timeout 30s http://localhost:8080/search -s /scripts/post.lua -- debug true
-
Идем в графану и наблюдаем нагрузку на мастер.
-
Ждем пока wrk отработает:
Running 5m test @ http://192.168.1.66:8080/search 1 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 5.83s 1.74s 13.29s 77.58% Req/Sec 3.57 4.74 30.00 82.50% 511 requests in 5.00m, 18.69MB read Requests/sec: 1.70 Transfer/sec: 63.79KB
-
Подключаем первый слэйв в приложении, добавлением в docker-compose.yml переменной окружения:
APP_DSN_SLAVE1: mysql_slave1
-
Перезапускаем контейнер с приложением и нагружаем ту же страницу тем же запросом, с помощью wrk:
sudo make app-reload sudo docker run --rm -v /root/scripts:/scripts williamyeh/wrk -t1 -c10 -d5m --timeout 30s http://localhost:8080/search -s /scripts/post.lua -- debug true
-
Идем в графану и наблюдаем нагрузку на слэйв.
-
Ждем пока wrk отработает:
Running 5m test @ http://192.168.1.66:8080/search 1 threads and 10 connections Thread Stats Avg Stdev Max +/- Stdev Latency 32.12ms 31.59ms 561.62ms 90.65% Req/Sec 373.34 147.37 610.00 68.45% 110834 requests in 5.00m, 433.90MB read Non-2xx or 3xx responses: 110834 Requests/sec: 369.39 Transfer/sec: 1.45MB
-
Получаем график нагрузки:
4. ОПЦИОНАЛЬНО: в качестве конфига, который хранит IP реплики сделать массив для легкого добавления реплики. Это не самый правильный способ балансирования нагрузки. Поэтому опционально.
Не очень понял, что именно нужно сделать. Если речь о конфиге приложения, я пока сделал отдельные переменные для каждого сервера БД. Если потребуется, заменю срезом. Пока пусть останется так.
5. Настроить 2 слейва и 1 мастер.
Пропускаю этот пункт, т.к. все уже было сделано в п.1
6. Включить row-based репликацию.
-
Добавляем в my.cnf мастера и обоих слейвов, строки:
binlog_format=ROW binlog-checksum=crc32
7. Включить GTID.
-
Добавляем в my.cnf мастера и обоих слейвов, строки:
gtid-mode=on enforce-gtid-consistency=true
8. Настроить полусинхронную репликацию.
-
Включаем динамическую загрузку модулей и полусинхронную репликацию с таймаутом 1с в my.cnf на мастере:
have_dynamic_loading=YES rpl_semi_sync_master_enabled=1 rpl_semi_sync_master_timeout=1000
-
Включаем динамическую загрузку модулей и полусинхронную репликацию в my.cnf на обоих слейвах:
have_dynamic_loading=YES rpl_semi_sync_master_enabled=1
-
Устанавливаем semisync плагин на мастере:
INSTALL PLUGIN rpl_semi_sync_master SONAME "semisync_master.so";
-
Устанавливаем semisync плагин на обоих слейвах:
INSTALL PLUGIN rpl_semi_sync_slave SONAME "semisync_slave.so";
-
Проверяем на всех хостах, установлен ли плагин:
SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE '%semi%';
-
Запускаем кластер и приложение:
sudo make app-up