Ранее в блоге на Хабре мы рассказывали о развитии нашего продукта — биллинга для операторов связи «Гидра», а также рассматривали вопросы работы с инфраструктурой и использования новых технологий. К примеру, мы рассмотрели плюсы Clojure, ситуации, когда стоит и не стоит использовать MongoDB и ограничения в PostgreSQL.
Сегодня речь пойдет о масштабировании. Разработчики open-source почтового приложения Nylas опубликовали в своем блоге материал о том, как им удалось масштабировать систему в 20 раз за три недели с помощью инструмента ProxySQL. Для этого им пришлось переехать с Amazon RDS на MySQL на EC2. Мы представляем вашему вниманию основные моменты этой интересной заметки.
Одним прекрасным днем…
История началась летом 2015. Солнце пробивалось сквозь туман, зависший над Сан-Франциско, по радио играл хит Bad Blood Тейлор Свифт и Кендрика Ламара. В распоряжении Nylas были лишь две машины синхронизации и одна база MySQL, управляемая через Amazon RDS. Это было самое простое решение, которое компании удалось на тот момент найти.
Инженерам компании удалось добиться хорошей производительности этой конфигурации так что они неспешно работали над улучшением логики синхронизации и телеметрии. Команда понимала все опасности преждевременной оптимизации продукта. Все считали, что лучший способ преуспеть в горизонтальном масштабировании системы (шардинге) – это откладывать его, насколько возможно. Поэтому, чтобы выиграть время, инженеры занимались оптимизацией работы с дисками, апгрейдом инстансов и сжатием данных
А затем появились пользователи
Все изменилось 5 октября 2015. В этот день вышел Nylas N1 – открытое email-приложение с красивым пользовательским интерфейсом и современной архитектурой с использованием плагинов. Ссылка на продукт была опубликована в Hacker News. По заверениям команды Nylas, это привело к появлению десятков тысяч новых пользователей за считанные минуты. А вместе с пользователями пришли большие объемы новой информации.
За час база на MySQL перестала справляться с такими объемами, а задержка работы API побила все рекорды. Пришлось временно отключить возможность регистрации. В компании еще не осознали всю глубину проблм, но одна вещь была очевидна всем: нужно масштабироваться как можно скорее.
На баррикады
В течение нескольких дней после запуска Nylas N1 удалось обнаружить сразу несколько узких мест в коде. Пришлось столкнуться с ограничениями Redis и MySQL на объемы вкладок. Инженеры узнали много нового и о работе с AWS.
Но самой серьезной проблемой была база данных. День и ночь команда Nylas пыталась исправить ошибки вроде утечки MySQL-соединений, а также плохо оптимизированные запросы, негативно влиявшие на производительность. Помимо того, чтобы залатать дыры, нужно было подумать о перспективе. Единственный вариант – применить горизонтальное масштабирование данных, чтобы обеспечить качественную поддержку новых пользователей. То есть все-таки пришло время шардинга системы.
Почему MySQL
В 2013, когда компания Nylas была основана, ее инженеры выбрали MySQL, не только исходя из специфических характеристик, но и потому что этот инструмент зарекомендовал себя в работе высоконагруженных проектов. Технологии Cassandra, MongoDB, и Postgres выглядели впечатляющими и быстро прогрессировали, но инженеры были уверены, что с помощью MySQL смогут создать систему обрабатывающую петабайты данных. Об этом говорил и опыт таких компаний как Facebook, Dropbox, Pinterest, и Uber. Использование MySQL позволяло опираться на опыт этих технологических гигантов.
Эта база проста в работе, а требования Nylas изначально были не особенно масштабными. Исходя из этого, было решено использовать
Время масштабировать
К моменту возникновения сложностей с производительностью инженеры уже сжали длинные текстовые колонки в базе, что помогло сэкономить 40% места на диске, а также перешли на использование 6-терабайтных дисков. Но InnoDB в СУБД MySQL имеет ограничение 2 ТБ на таблицу, что представляло собой проблему.
Инженеры рассматривали вариант с использованием Amazon Aurora — MySQL-совместимого хранилища с автомасштабированием до 64 Тб. Однако даже в этом случае, при превышении заданного лимита в 64 терабайта, чтобы уместиться в ограничения InnoDB, пришлось бы разбивать данные по различным таблицам. К тому же команде не хотелось настолько сильно зависеть от технологий Amazon в долгосрочной перспективе.
Вместо этого инженеры Nylas осуществили горизонтальное масштабирование с помощью разбиения данных по простой схеме, а также с помощью создания дополнительных MySQL-кластеров, работающих на версиях базы от Oracle на обычных инстансах EC2. Главной целью была простота конфигурации, что позволило завершить оптимизацию за три недели.
Прощай, RDS
В процессе масштабирования было решено отказаться от RDS. Причина в недостатке возможностей по контролю, а также неудачный опыт реализации решений для оптимизации. Казалось, что RDS — просто не решение для создания надежных систем с нулевым временем простоя.
Кроме того, в RDS крайне ограничены возможности настройки репликации. Да, в ней есть опция multi-AZ для синхронного копирования данных, но производительность таких операций оставляла желать лучшего. Однажды ее использование привело к 3-часовому простою в работе приложения Nylas. Пришлось связаться с инженерами Amazon и ждать, пока они исследуют проблему.
Также в Nylas пробовали создавать асинхронные копии для заданной мастер-базы, но оказалось, что активируется такая копия с задержкой в несколько минут. RDS не поддерживает последовательное копирование, которое планировалось использовать для разнесения шардов по инстансам базы.
Вместо этого выяснилось, что многие задачи можно успешно решать с помощью EC2. Да, с отдельными инстансами могут возникать проблемы, но можно было, по крайней мере, создать архитектуру базы, устойчивую к влиянию описанных выше проблем. В компании решили отказаться от RDS и управлять базой самостоятельно.
Запуск MySQL на EC2
Перевод MySQL на EC2 – не совсем простой процесс. Нужно было написать собственные скрипты для управления задачами, которые в RDS были автоматизированы — например провижининг новых серверов, создание и распределение slave-инстансов в случае сбоя master-базы и создание и восстановление резервных копий. В Nylas написали такие скрипты с нуля, но существует и ряд открытых инструментов, решающих те же задачи.
Один из шард-кластеров в MySQL состоит из управляющего узла и подчиненного узла. Второй поддерживает обновления через лог копирования MySQL, и обычно используется для бэкапов. В процессе перенастройки системы, перевода узлов на разные типы инстансов, кластер должен иметь более одного slave-инстанса. Если дополнительные слейвы не активны, они помечены тегом «debug» EC2, что исключает их из мониторинга, бэкапов и других процессов для активных узлов.
Аварийное переключение
Для каждого кластера базы нужно было предусмотреть ситуацию, когда узел мастера отказывается правильно функционировать. Это может произойти, когда процессы в базе сбиваются или виснут, хост становится недоступен, или происходит что-то еще в таком духе. На этот случай нужно иметь в запасе возможность восстановления, вручную или автоматически, когда slave-узел заменяет на время master-узел и получает одновременно запросы чтения и записи.
Чтобы решить эту задачу, был написан специальный скрипт, позволяющий инженерам вручную работать с slave-узлом таким образом. Как только в команде убедились, что эту операцию можно проводить, не опасаясь потерь данных, скрипт интегрировали в стандартный инструмент mysqlfailover, с некоторыми добавлениями, позволяющими идентифицировать принадлежность узлов. Если происходит сбой в узле мастера, mysqlfailover узнает об этом через механизм проверки состояния и определяет слейв как нового мастера. Конструкция mysqlfailover, помимо прочего, обновляет теги EC2 для отображения новой конфигурации.
Восстановление работает автоматически, но инженеры получают оповещения. После чего, один из них может вручную выявлять узел, на котором произошел сбой, и преобразовывать его в новый слейв для кластера. Если такой возможности нет — например, в случае повреждения данных, предыдущий мастер выводится из эксплуатации, а новый слейв создается из самого последнего бэкапа. Система стала устойчивой к сбоям, и ей стало легче управлять.
Шардинг приложения
При переходе с одной базы данных к нескольким базам данных на разных машинах необходимо разработать новый механим маппинга ID объектов на конкретные серверы. Это особенно актуально, если приложение ссылается на базу по первичному ключу во вторичном хранилище, как это происходит у Redis.
Инженеры Nylas подобрали простое решение этой задачи: значение первичного ключа таблицы — это 64-битное целое число, верхние же биты содержат ID шарда, нижние биты являются локальным счетчиком автоматических числовых ключей.
|<--16bits-->|<--------48bits------->| +------------+-----------------------+ | shard id | autoincrement | +------------+-----------------------+
Это позволяет легко рассчитывать шард-ID по первичному ключу. Запрос на любой объект может быть направлен в правильный шард просто через первичный ключ. Чем проще система – тем надежнее работа с ней в перспективе.
Например, уровень аутентификации приложения способен конвертировать хэшированные токены доступа в ID аккаунта для доступа к API. Команде проекта удалось провести шардинг сервиса без изменения ключевых его компонентов. Поскольку движок Nylas так же как и приложение — открытый продукт, любой желающий может изучить особенности реализации такой системы на GitHub.
Создание новых шардов
Каждый логический шард – это отдельно пронумерованная MySQL-схема. Все шарды имеют одинаковую табличную структуру и разные шарды обычно размещены в одном инстансе.
При создании нового шарда рассчитать биты высокого порядка просто через:
N = (shard_id<<48) + 1
Значение автоматических чистовых ключей рассчитывается так:
ALTER TABLE mailsync_<shard_id>.<tablename> AUTO_INCREMENT=N
Новые ряды в таблице, соответственно, будут иметь первичные ключи N, N+1 и так далее. В данном случае таблицы базы имеют автоматические ключ, начинающиеся со значения 1. Инженеры Nylas советуют при использовании этого метода обращать внимание на ошибку MySQL под номером #199. Этому багу уже 13 лет, но он до сих пор не исправлен. Чтобы избежать проблем с ним, код приложения должен быть защищен от некорректных значений автоматических ключей — вот здесь представлено описание решения этой задачи (на английском).
Минимизация времени ожидания при отказе
После проведения шардинга, была обнаружена еще одна проблема. Конфигурация mysqlfailover использовала вторичный IP-адрес Amazon VPC в качестве виртуального IP, чтобы отправлять трафик приложения к узлу мастера. Но обнаружение сбоя в узле и переключение трафика через этот виртуальный айпи занимало порядка 10 минут. Все запросы в течение этого промежутка, разумеется, отклонялись.
Следовательно, нужно было реализовать гладкое аварийное переключение. Что-то наподобие того, как HAProxy может удерживать запросы при перезапуске сервиса приложения, только в приложении к MySQL.
Здесь можно использовать HAProxy в сочетании с хранилищем значений ключей, а можно использовать кластерные инструменты MySQL: Orchestrator или Galera. В данном случае компания выбирала самое простое решение из доступных. В идеале – чтобы не было дополнительных сервисов, которые надо обслуживать.
ProxySQL
Такое решение было найдено в ProxySQL – новом инструменте с открытой лицензией. Если не вдаваться в подробности, его можно рассматривать как HAProxy, только заточенный под запросы SQL. После того, как Nylas перешла на работу с ProxySQL, время ожидания при переключении узлов снизилось с 10 минут до 10 секунд.
Переключение без ProxySQL (простой 10 минут)
Переключение с ProxySQL (простой 10 секунд)
В процессе обнаружились другие преимущества работы с ProxySQL:
- динамическая маршрутизация;
- автоматические ограничения для запросов или транзакций, предотвращающие случайные спады производительности;
- статистика запросов: позволяет сравнивать запросы по узлам приложения, то есть понять, как загрузка распределена по шардам;
- возможность резко снижать количество экспортных соединений к актуальной базе данных машины.
Инженеры Nylas используют ProxySQL локально для каждого сервиса, подключающегося к базе, включая внутренние инструменты управления и скрипты. Использование прокси в локальном режиме вместо централизованного позволяет добиться встроенной избыточности, а также сводит к минимуму вероятность возникновения задережек трафика на прокси. В итоге шарды в системе могут динамически меремещаться между экземплярами базы данных — время простоя кластера при этом не превышает 12 секунд.
Перевод данных на другую платформу
В марте 2016 года компания осуществила миграцию шард 0 (RDS) на “in-house” MySQL на EC2 посредством функции репликации. Это позволило обновить первичные ключи для самой крупной таблицы с 32 до 64 бит, чего нельзя было представить с RDS. Первичное пространство ключей (4 млрд рядов) было выбрано полностью за пару месяцев.
Планы
Облачная инфраструктура Nylas продолжает динамично развиваться. После обновления уровня базы данных пришло время обновления уровня самого приложения. Текущий проект включает разделение логики синхронизации по нескольким микросервисам и обновление топологии процессов с магистральным источником данных, типа Kafka. Есть также вариант реализовать денормализацию схем для уровня хранилища API, чтобы сделать дальнейшую работу с приложением более гибкой.
Другие технические статьи от «Латеры»:
- Автоматизируем учет адресов и привязок в IPoE-сетях
- DoS своими силами: К чему приводит бесконтрольный рост таблиц в базе данных
- Архитектура open source-приложений: Как работает nginx
- Как повысить отказоустойчивость биллинга: Опыт «Гидры»
Автор: Латера Софтвер