diff --git a/README.md b/README.md index e69de29..b0223ff 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,18 @@ +## Домашние задания по курсу +### [01. Заготовка для социальной сети](test/dz001/README.md) (ЗАЧЕТ) +### [02. Производительность индексов](test/dz002/README.md) (ЗАЧЕТ) + +----- +### [03. Полусинхронная репликация](test/dz003/README.md) (в процессе) + +--- +### [04. Лента новостей социальной сети](test/dz004/README.md) (не начата) +### [05. Репликация из MySQL в tarantool](test/dz005/README.md) (не начата) +### [06. Масштабируемая подсистема диалогов](test/dz006/README.md) (не начата) +### [07. Онлайн обновление ленты новостей](test/dz007/README.md) (не начата) +### [08. Разделение монолита на сервисы](test/dz008/README.md) (не начата) +### [09. Отказоустойчивость приложений](test/dz009/README.md) (не начата) +### [10. Сервис счетчиков](test/dz010/README.md) (не начата) +### [11. Внедрение docker и consul](test/dz011/README.md) (не начата) +### [12. Мониторинг](test/dz012/README.md) (не начата) +### [13. Проектная работа](test/dz013/README.md) (не начата) \ No newline at end of file diff --git a/cicd/init.sh b/cicd/init.sh old mode 100755 new mode 100644 diff --git a/test/dz001/README.md b/test/dz001/README.md index 548660f..d13fde1 100644 --- a/test/dz001/README.md +++ b/test/dz001/README.md @@ -1,3 +1,4 @@ +#[ЗАЧТЕНА](https://otus.ru/learning/61597/#/homework-chat/9332/) # Заготовка для социальной сети Цель: В результате выполнения ДЗ вы создадите базовый скелет социальной сети, который будет развиваться в дальнейших ДЗ. diff --git a/test/dz002/README.md b/test/dz002/README.md new file mode 100644 index 0000000..080c577 --- /dev/null +++ b/test/dz002/README.md @@ -0,0 +1,32 @@ +#[ЗАЧТЕНА](https://otus.ru/learning/61597/#/homework-chat/9333/): [ОТЧЕТ](REPORT.md) +# Производительность индексов +Цель: В результате выполнения ДЗ вы создадите набор тестовых данных для проведения нагрузочного тестирования, подберете наиболее подходящие индексы и проведете тесты производительности. + +### В данном задании тренируются навыки: +- генерация тестовых данных; +- работа с индексами; +- нагрузочное тестирование; + +### План выполнения: +1) Сгенерировать любым способ 1,000,000 анкет. Имена и Фамилии должны быть реальными (чтобы учитывать селективность индекса) +2) Реализовать функционал поиска анкет по префиксу имени и фамилии (одновременно) в вашей социальной сети (запрос в форме firstName LIKE ? and secondName LIKE ?). Сортировать вывод по id анкеты. Использовать InnoDB движок. +3) С помощью wrk провести нагрузочные тесты по этой странице. Поиграть с количеством одновременных запросов. 1/10/100/1000. +4) Построить графики и сохранить их в отчет +5) Сделать подходящий индекс. +6) Повторить пункт 3 и 4. +7) В качестве результата предоставить отчет в котором должны быть: + - графики latency до индекса; + - графики throughput до индекса; + - графики latency после индекса; + - графики throughput после индекса; + - запрос добавления индекса; + - explain запросов после индекса; + - объяснение почему индекс именно такой; + +ДЗ принимается в виде отчета по выполненной работе. +Критерии оценки: Оценка происходит по принципу зачет/незачет. + +###Требования: +Правильно выбраны индексы. +Нагрузочное тестирование проведено и результаты адекватны. +Рекомендуем сдать до: 01.02.2021 \ No newline at end of file diff --git a/test/dz002/REPORT.md b/test/dz002/REPORT.md new file mode 100644 index 0000000..b64c6bf --- /dev/null +++ b/test/dz002/REPORT.md @@ -0,0 +1,42 @@ +### 1. Графики Latency и Kb/s от кол-ва одновременных запросов, до и после установки индексов. + ![mountains](img/diagramms.jpg "Диаграммы нагрузки") +### 2. В случае использования индекса indNameSurname: + - #### Запрос на добавление индексов: + ``` + ALTER TABLE `users` ADD INDEX `indNameSurname` (`Name`, `Surname`); + ``` + - #### Explain запроса: + ``` + EXPLAIN SELECT + users.id as id, + users.name as name, + users.surname as surname, + users.birthdate as birthdate, + users.gender as gender, + users.city as city + FROM + users + WHERE users.Name LIKE "ан%" AND users.Surname LIKE "ан%" + ORDER BY id + ``` +### 3. В случае использования индекса indSurnameName: + - #### Запрос на добавление индексов: + ``` + ALTER TABLE `users` ADD INDEX `indSurnameName` (`Surname`, `Name`); + ``` + - #### Explain запроса: + ``` + EXPLAIN SELECT + users.id as id, + users.name as name, + users.surname as surname, + users.birthdate as birthdate, + users.gender as gender, + users.city as city + FROM + users + WHERE users.Name LIKE "ан%" AND users.Surname LIKE "ан%" + ORDER BY id + ``` +#### 4. РезюмеЖ +Для конечной оптимизации используется составной индекс `('Surname', 'Name')` потому, что в запросе используется объединение AND условий LIKE ?%. Mysql в этом случае ищет по составному индексу и объединяет строки без сортировки. Именно поэтому, приходится принудительно сортировать результаты. Порядок полей выбран с точки зрения селективности. В реальной обстановке поле «Фамилия» все же более селективно чем «Имя». Плюс к этому, мы видим в EXPLAIN запроса, что в случае использования индекса `('Surname', 'Name')` mysql применяет Multi-Range Read оптимизацию, позволяющую линеаризовать процедуру чтения с диска. Возможно за этот счет значительно повысилась скорость передачи данных? по сравнению с индексом `('Name', 'Surname')`. \ No newline at end of file diff --git a/test/dz002/post.lua b/test/dz002/scripts/post.lua similarity index 100% rename from test/dz002/post.lua rename to test/dz002/scripts/post.lua diff --git a/test/dz003/README.md b/test/dz003/README.md index fc3a057..af76519 100644 --- a/test/dz003/README.md +++ b/test/dz003/README.md @@ -1,3 +1,4 @@ +#[в процессе](https://otus.ru/learning/61597/#/homework-chat/9334/): [ОТЧЕТ](REPORT.md) #Полусинхронная репликация В результате выполнения ДЗ вы настроите полусинхронную репликацию, протестируете ее влияние на производительность системы и убедитесь, что теперь вы не теряете транзакции в случае аварии. @@ -23,7 +24,7 @@ 12) Выбираем самый свежий слейв. Промоутим его до мастера. Переключаем на него второй слейв. 13) Проверяем, есть ли потери транзакций. -Результатом сдачи ДЗ будет в виде исходного кода на github и отчета в текстовом виде, где вы отразите как вы выполняли каждый из пунктов. +Результатом сдачи ДЗ будет в виде исходного кода на github и [отчета в текстовом виде](REPORT.md), где вы отразите как вы выполняли каждый из пунктов. Критерии оценки: Оценка происходит по принципу зачет/незачет. ### Требования: diff --git a/test/dz003/REPORT.md b/test/dz003/REPORT.md new file mode 100644 index 0000000..80bbcfc --- /dev/null +++ b/test/dz003/REPORT.md @@ -0,0 +1,225 @@ +### 1. Настраиваем асинхронную репликацию. +Я решил сделать сразу три хоста - один мастер и два слейва, чтобы к этому не возвращаться в 5-м пункте. +- #### [my.cnf мастера](../../cicd/mysql/mysql_master.conf): +``` +[mysqld] +skip-host-cache +skip-name-resolve +server-id = 1 +log_bin = /var/log/mysql/mysql-bin.log +binlog_do_db = app +``` +- #### [my.cnf первого слэйва](../../cicd/mysql/mysql_slave1.conf): +``` +[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 +``` +- #### [my.cnf первого слэйва](../../cicd/mysql/mysql_slave2.conf): +``` +[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 +``` +- #### [Инициализация кластера](../../cicd/init.sh): + - на мастере создаем базу, пользователя для работы приложения и пользователя для репликации: + ``` + 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](../dz002/REPORT.md). Тем более, что все инструменты для этого уже есть в наличии (wrk). +- #### Добавил в [структуру конфига](../../internal/models/config.go) адреса мастера и двух слейвов: + ``` + type DSN struct { + Master string + Slave1 string + Slave2 string + Port string + User string + Pass string + Base string + } + ``` +- #### В [хэндлере страницы app:port/search](../../internal/handlers/handlers.go) создал селектор, который направляет запросы чтения на первый слейв, если он присутствует в конфигурации: + ``` + 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 скрипта](scripts/post.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 + ``` +- #### Идем в [графану](http://localhost:3001/) и наблюдаем нагрузку на мастер. +- #### Ждем пока 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 + ``` +- #### Идем в [графану](http://localhost:3001/) и наблюдаем нагрузку на слэйв. +- #### Ждем пока 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 + ``` +- #### Получаем график нагрузки: +![График нагрузки на контейнерах приложения](img/dz003_part1.jpg "График нагрузки на контейнерах приложения") + +### 4. ОПЦИОНАЛЬНО: в качестве конфига, который хранит IP реплики сделать массив для легкого добавления реплики. Это не самый правильный способ балансирования нагрузки. Поэтому опционально. +Не очень понял, что именно нужно сделать. Если речь о конфиге приложения, я пока сделал отдельные переменные для каждого сервера БД. Если потребуется, заменю срезом. Пока пусть останется так. +### 5. Настроить 2 слейва и 1 мастер. +Пропускаю этот пункт, т.к. все уже было сделано в п.1 +### 6. Включить row-based репликацию. +- #### Добавляем в my.cnf мастера строки: + ``` + binlog_format=ROW + binlog-checksum=crc32 + ``` +- #### Добавляем в my.cnf обоих слейвов строки: + ``` + binlog_format=ROW + binlog-checksum=crc32 + ``` +- #### Запускаем кластер и приложение: + ``` + sudo make app-up + ``` +### 7. Включить GTID. +- #### Добавляем в my.cnf мастера строки: + ``` + gtid-mode=on + enforce-gtid-consistency=true + ``` +- #### Добавляем в my.cnf обоих слейвов строки: + ``` + gtid-mode=on + enforce-gtid-consistency=true + ``` +- #### Запускаем кластер и приложение: + ``` + sudo make app-up + ``` +### 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 + ``` +### 9. Создать нагрузку на запись в любую тестовую таблицу. На стороне, которой нагружаем считать, сколько строк мы успешно записали. +### 10. С помощью kill -9 убиваем мастер MySQL. +### 11. Заканчиваем нагрузку на запись. +### 12. Выбираем самый свежий слейв. Промоутим его до мастера. Переключаем на него второй слейв. +### 13. Проверяем, есть ли потери транзакций. + + + + +Встроен запуск prometeus, grafana и т.д. +Графана доступна на http://localhost:3001/ + +$ sudo docker run --rm -v /root/scripts:/scripts williamyeh/wrk -t1 -c10 -d5m --timeout 30s http://192.168.1.66:8080/search -s /scripts/post.lua -- debug true +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 + +$ sudo make app-reload +... +... +... +$ sudo docker run --rm -v /root/scripts:/scripts williamyeh/wrk -t1 -c10 -d5m --timeout 30s http://192.168.1.66:8080/search -s /scripts/post.lua -- debug true +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 \ No newline at end of file diff --git a/test/dz003/report.md b/test/dz003/report.md deleted file mode 100644 index 634915b..0000000 --- a/test/dz003/report.md +++ /dev/null @@ -1,27 +0,0 @@ -Встроен запуск prometeus, grafana и т.д. -Графана доступна на http://localhost:3001/ - -$ sudo docker run --rm -v /root/scripts:/scripts williamyeh/wrk -t1 -c10 -d5m --timeout 30s http://192.168.1.66:8080/search -s /scripts/post.lua -- debug true -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 - -$ sudo make app-reload -... -... -... -$ sudo docker run --rm -v /root/scripts:/scripts williamyeh/wrk -t1 -c10 -d5m --timeout 30s http://192.168.1.66:8080/search -s /scripts/post.lua -- debug true -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 \ No newline at end of file