- PVSM.RU - https://www.pvsm.ru -
Проектируя, разрабатывая и запуская наш новый большой проект — «Битрикс24» [1], мы не только хотели сделать по-настоящему классный сервис для командной работы (к тому же еще и бесплатный — до 12 пользователей), но еще и собрать и накопить опыт по эксплуатации облачных веб-сервисов, «прокачать» свою компетенцию в разработке высоконагруженных отказоустойчивых проектов и — самое главное — поделиться этими знаниями как с нашими партнерами [2], так и со всеми веб-разработчиками, кому близка тема «хайлоада». :)
Конечно, в одной статье (и даже не в одной) невозможно описать универсальный «рецепт», который бы подошел абсолютно для всех проектов: для кого-то важнее производительность (иногда — даже в ущерб надежности), для кого-то — наоборот, отказоустойчивость превыше всего, где-то много маленьких таблиц, где-то — большой объем данных…
Мы постарались описать те «изюминки», которые не раз помогали нам в работе в решении тех или иных практических задач. Надеемся, они окажутся полезными и для вас. :)
Начнем по порядку.
Мы не раз уже говорили и писали [3] о том, что «Битрикс24» [1] развернут в Амазоне. И раз мы так любим и активно используем различные облачные сервисы, первый же вопрос…
1. Почему не используем RDS?
Amazon Relational Database Service [4] (Amazon RDS) — облачная база данных. Есть поддержка MS SQL, Oracle и — что было интересно нам — MySQL.
Долго присматривались, хотели использовать в своем проекте… В итоге отказались от этой идеи. Несколько ключевых причин:
Все это не означает того, что RDS — плохой сервис, и его не надо никогда использовать. Это не так. Он не подошел конкретно для нас. И, возможно, для кого-то будет гораздо проще обеспечить масштабирование и отказоустойчивость именно средствами Амазона.
2. Master-Slave? Нет, Master-Master!
Стандартная схема репликации в MySQL «Master-Slave» давно и успешно применяется на многих проектах и решает несколько задач: масштабирование нагрузки (только на чтение) — перераспределение запросов (SELECT'ов) на слейвы, отказоустойчивость.
Но решает — не полностью.
1. Хочется масштабировать и запись.
2. Хочется иметь надежный failover и продолжать работу автоматически в случае каких-либо аварий (в master-slave в случае аварии на мастере нужно один из слейвов в ручном или полу-автоматическом режиме переключить в роль мастера).
Чтобы решить эти задачи, мы используем «мастер-мастер» репликацию. Не буду сейчас повторяться, этой технике у нас недавно был посвящен отдельный пост на Хабре [5].
3. MySQL? Нет, Percona Server!
Первые несколько месяцев (на прототипах, в процессе разработки, в начале закрытого бета-тестирования сервиса) мы работали на стандартном MySQL. И чем дольше работали, тем больше присматривались к различным форкам. Самими интересными, на наш взгляд, были Percona Server [6] и MariaDB [7].
Выбрали мы в итоге Перкону — конечно, из-за похожего «перевернутого» логотипа. ;)
… и нескольких фич, которые оказались крайне важными для нас:
Полный список можно посмотреть на сайте в разделе «Percona Server Feature Comparison» [9].
Важный момент — переход со стандартного MySQL на Percona Server вообще не потребовал изменения какого-либо кода или логики приложения.
А, вот, сам процесс «переезда» был достаточно интересным. И благодаря использованию схемы с «мастер-мастер» репликацией [5], прошел совершенно незаметно для наших пользователей. Даунтайма просто не было.
Схема переезда была такой:
4. MyISAM? InnoDB?
Тут все просто.
* * *
Переходим от архитектурных вопросов к более практическим. :)
5. Все ли данные нужно реплицировать? Нет, не все.
Почти в любом проекте есть некритичные для потери или восстанавливаемые данные. В том числе — и в базе данных.
В нашем случае такими данными были сессии. Что было плохого в том, что реплицировалось все подряд?
Исключение этих данных из репликации полностью решило проблему.
Как исключать? Есть разные способы.
1. На уровне приложения в том коннекте, где идет работа с таблицами, которые мы хотим исключить из репликации, выполняем:
SET sql_log_bin = 0;
2. Более простой и понятный способ — указать исключение в конфигурационном файле MySQL.
replicate-wild-ignore-table = %.b_sec_session
Такая конструкция исключает из репликации таблицы b_sec_session во всех базах.
Все немножко более сложно в том случае, если вам нужна более сложная логика. Например, не реплицировать таблицы table во всех базах, кроме базы db.
В таком случае вам придется немного порисовать схемки наподобие тех, которые дает MySQL, чтобы составить правильную комбинацию опций — фильтров.
6. Тип репликации.
Много споров вызывает вопрос, использовать ли STATEMENT-based или ROW-based репликацию. И тот, и другой вариант обладают и плюсами, и минусами [12].
По умолчанию в MySQL (Percona) 5.5 используется STATEMENT-based репликация.
На нашем приложении в такой конфигурации мы регулярно видели в логах строки: «Statement may not be safe to log in statement format».
Кроме того, сравнение двух баз в мастер-мастер репликации показывало, что могут появляться расхождения в данных. Это, конечно, было недопустимо.
В MySQL есть интересное решение, которое нас полностью устроило — использовать MIXED формат бинлога [13]:
binlog-format = mixed
В этом случае по умолчанию репликация идет в режиме STATEMENT и переключается в ROW как раз в случае таких небезопасных операций.
7. Репликация сломалась. Что делать?
Репликация иногда все-таки ломается. Особенно страшно (поначалу :)) это звучит при работе с «мастер-мастер».
На самом деле, ничего страшного нет. Правда. :)
В первую очередь нужно помнить о том, что описанная схема «мастер-мастер» репликации [5] — это на самом деле просто две обычные «master-slave» репликации. Да, с некоторыми нюансами, но большинство практик, используемых в стандартной схеме, работают и здесь.
Самая простая (и самая часто случающаяся) проблема — ошибка вида «1062 Error 'Duplicate entry'».
Причины могут быть разными. Например, мы в случае какой-либо аварии с базой переключаем траффик на другой ДЦ. Если запрос уже был выполнен в ДЦ 1, но не успел среплицироваться в ДЦ 2 и был выполнен там повторно — да, получим именно такую ошибку.
Лечится выполнением вот таких команд на слейве:
STOP SLAVE;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
START SLAVE;
Тем самым пропускаем лишний запрос. Далее смотрим состояние репликации:
SHOW SLAVE STATUSG
Если требуется, повторяем процедуру.
* * *
Да, мы сейчас детально рассматриваем самый простой вариант. Все бывает значительно хуже — рассыпается файловая система, бьются файлы и т.п.
Универсального рецепта «как все починить» нет. Но всегда важно помнить следующее:
8. Как поднимать из бэкапа один из серверов в «мастер-мастер» репликации?
Что же делать, если в схеме с двумя мастерами все-таки что-то пошло не так (например, во время аварии в Амазоне несколько дней назад [14] у нас необратимо повредились файловые системы на нескольких серверах)?
Решение «в лоб» — перелить данные из одного сервера на другой и запустить репликацию с нуля — слишком долго.
В Амазоне мы используем механизмы снэпшотов дисков и создание образов (AMI) целых машин [15]. Это позволяет очень быстро развернуть полную копию нужного сервера — например, по состоянию на несколько часов назад.
Если мы просто развернем машину из бэкапа, мы получим интересный эффект: мы начнем читать данные из бинлогов «живого» мастера (с момента, когда создавался бэкап), но прочитаем лишь половину их, так как по умолчанию записи с сервера с тем же server-id (из «будущего» относительно времени бэкапа) реплицироваться не будут. Это делается для того, чтобы избежать «зацикливаний» в «мастер-мастер».
Поступаем так:
1. Весь траффик идет на «живой» ДЦ. На тот сервер, который мы восстанавливаем нагрузки нет.
2. На сервере, поднятом из бэкапа, сразу останавливаем mysqld и вписываем в конфиг:
skip-slave-start
replicate-same-server-id
#log-slave-updates = 1 ; комментируем!
3. Запускаем mysqld и стартуем репликацию.
4. После того, как данные синхронизированы, возвращаем конфиг в исходное состояние:
#skip-slave-start
#replicate-same-server-id
log-slave-updates = 1
5. Так как у нас — «мастер-мастер», нам нужно запустить репликацию и в обратную сторону. Останавливаем репликацию на том сервере, который мы восстанавливали, и выполняем:
SHOW MASTER STATUS;
Если репликацию не остановим, данные будут меняться.
6. Стартуем с нужной позиции репликацию на первом (живом) сервере:
STOP SLAVE;
CHANGE MASTER TO MASTER_LOG_FILE='...', MASTER_LOG_POS = ...;
START SLAVE;
Вписываем данные, полученные в пункте 5.
7. Стартуем репликацию и на втором сервере.
9. Где баланс между производительностью и надежностью репликации?
Есть много опций в MySQL / Percona, которые позволяют кардинально повысить надежность и сохранность данных в случае, например, внезапных ребутов. Практически все они столь же кардинально снижают быстродействие системы.
Мы для себя нашли баланс в такой комбинации опций:
sync_binlog = 1
sync_master_info = 0
sync_relay_log = 0
sync_relay_log_info = 0
innodb-flush-log-at-trx-commit = 2
Бинлог для нас критически важен, поэтому sync_binlog = 1. Но при этом бинлоги хранятся на отдельном диске в системе, поэтому запись на этот диск не снижает производительность системы в целом.
10. Как вообще оценивать производительность системы?
Если у нас есть большие «тяжелые» запросы, то, конечно, мы банально ориентируемся на время их выполнения.
Чаще же (и в нашем случае — именно так) система обрабатывает много-много мелких запросов.
Конечно, можно использовать различные синтетические тесты для оценки производительности системы. И некоторую оценку они дадут. Но ведь хочется иметь какие-то реальные показатели (желательно — в цифрах :)), которые можно было бы применять «в бою».
В Percona Server есть замечательный инструмент:
SELECT * FROM INFORMATION_SCHEMA.QUERY_RESPONSE_TIME;
+----------------+-------+----------------+
| time | count | total |
+----------------+-------+----------------+
| 0.000001 | 0 | 0.000000 |
| 0.000010 | 6555 | 0.024024 |
| 0.000100 | 56132 | 2.326873 |
| 0.001000 | 23165 | 6.686421 |
| 0.010000 | 9755 | 39.737027 |
| 0.100000 | 1437 | 40.831493 |
| 1.000000 | 141 | 31.785571 |
| 10.000000 | 9 | 17.891514 |
| 100.000000 | 0 | 0.000000 |
| 1000.000000 | 0 | 0.000000 |
| 10000.000000 | 0 | 0.000000 |
| 100000.000000 | 0 | 0.000000 |
| 1000000.000000 | 0 | 0.000000 |
| TOO LONG | 0 | TOO LONG |
+----------------+-------+----------------+
14 rows in set (0.00 sec)
Такая гистограмма распределения времени выполнения запросов очень хорошо помогает оценивать общее состояние системы.
Например, мы для себя определили некий критический порог — не более 5% запросов (от общего числа) с временем выполнения более 0.01 сек.
Чтобы отслеживать это состояние в динамике, написали простой плагин к Munin'у, который как раз и рисует график по данному соотношению. Очень удобно, и — главное — это живая понятная метрика.
11. Сбалансированность по памяти.
Настройки MySQL должны быть такими, чтобы потребление памяти было сбалансировано!
Вроде, простое и понятное правило, но о нем часто забывают. Каюсь, сами пару раз (в начале, на прототипе :)) получили OOM (Out of memory) и — как следствие — «убитый» операционной системой процесс mysqld.
В идеале — процесс mysqld должен работать так, чтобы полностью помещаться в оперативной памяти и не оперировать свопом.
Обязательно — все процессы системы должны помещаться в память+swap.
Зачастую подсчеты, сколько памяти может потребить mysqld, оказываются для многих неочевидными.
Формула примерно такова:
Если не очень хотите считать :), можно воспользоваться скриптом mysqltuner.pl, который помимо этой информации покажет много других данных по системе, безопасности, производительности и т.п.
# wget mysqltuner.pl
# perl mysqltuner.pl
* * *
Спасибо, что дочитали до этого места! :)
Мы рассмотрели лишь некоторую часть практических вопросов и приемов, которые мы используем в работе «Битрикс24» [1]. В том числе благодаря им, сервис растет и развивается.
Надеемся, что наш опыт поможет и вам в создании и развитии ваших проектов.
И уже сейчас видно, что объема одной статьи совершенно недостаточно. В ближайшее время постараемся продолжить тему использования MySQL в больших проектах, поделимся новыми рецептами и опишем наиболее интересные и востребованные темы более подробно.
Автор: adamant
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/mysql/10799
Ссылки в тексте:
[1] Image: http://www.bitrix24.ru
[2] нашими партнерами: http://www.1c-bitrix.ru/partners/
[3] писали: http://habrahabr.ru/company/bitrix/blog/142859/
[4] Amazon Relational Database Service: http://aws.amazon.com/rds/
[5] мы недавно писали: http://habrahabr.ru/company/bitrix/blog/146490/
[6] Percona Server: http://www.percona.com/
[7] MariaDB: http://mariadb.org/
[8] организация софтверного рейда: http://habrahabr.ru/company/bitrix/blog/130096/
[9] «Percona Server Feature Comparison»: http://www.percona.com/software/percona-server/feature-comparison/
[10] Evaluation of Database-Level Replication and Binary Logging Options: http://dev.mysql.com/doc/refman/5.5/en/replication-rules-db-options.html
[11] Evaluation of Table-Level Replication Options: http://dev.mysql.com/doc/refman/5.5/en/replication-rules-table-options.html
[12] и плюсами, и минусами: http://dev.mysql.com/doc/refman/5.5/en/replication-sbr-rbr.html
[13] MIXED формат бинлога: http://dev.mysql.com/doc/refman/5.5/en/binary-log-mixed.html
[14] аварии в Амазоне несколько дней назад: http://venturebeat.com/2012/06/29/amazon-outage-netflix-instagram-pinterest/
[15] механизмы снэпшотов дисков и создание образов (AMI) целых машин: http://habrahabr.ru/company/bitrix/blog/127932/
Нажмите здесь для печати.