Масштабирование MySQL в Slack с помощью Vitess

Это история о том, как Slack изменил архитектуру хранения данных на Vitess – систему горизонтального масштабирования для MySQL. Миграция на Vitess началась в 2017 году, и сейчас Vitess обслуживает 99% общей нагрузки, переход планируется завершить к концу 2020 года. В этом посте мы обсудим проектирование и технические проблемы, связанные с выбором и внедрением Vitess, а также обзор нашего текущего использования данного сервиса.

Доступность, производительность и масштабируемость хранилища данных имеют критическое значение для Slack. Каждое сообщение сохраняется до того, как оно будет отправлено и показано другим участникам канала, поэтому доступ к хранилищу должен быть очень быстрым и очень надежным.

Сегодня мы обслуживаем 2,3 миллиона запросов в секунду в периоды пиковых нагрузок. 2 миллиона из этих запросов – это чтение, а 300 тысяч – запись. Среднее время ожидания составляет 2 мс, а задержка p99 (99-й процентиль) –11 мс.

НАЧАЛО

Slack начинался как простой LAMP стек: Linux, Apache, MySQL и PHP. Все данные хранились в трех основных кластерах на базе MySQL:

Шарды: содержали практически все данные клиентов, связанные с использованием Slack. Данные были разделены и масштабированы горизонтально по идентификатору рабочего пространство (workspace id). Все данные рабочего пространства хранились на одном шарде. Каждый шард содержал тысячи рабочих пространств и все их данные, включая сообщения и каналы.

Кластер метаданных: хранил таблицу маппинга рабочих пространств с шардами.

Сливной кластер: хранил все остальные данные, не относящиеся к какому-либо рабочему пространству.

Шардинг контролировался нашим монолитным веб-приложением, которое управляло всем доступом к данным и содержало логику поиска метаданных для конкретного рабочего пространства, а затем создания соединения с соответствующим шардом.

На рисунке ниже представлен обзор исходной архитектуры базы данных.

ПРЕИМУЩЕСТВА

У такой active-active конфигурации есть много преимуществ, которые позволили нам успешно масштабировать сервис:

Высокая доступность: когда возникают сбои при подключении к одному из узлов, приложение может повторить запрос к другому узлу, поскольку оба узла могут выполнять операции чтения и записи.

Высокая скорость разработки продукта: использование модели хранения всех данных рабочего пространства на одном хосте было интуитивно понятным для разработки нового функционала.

Легкая отладка: разработчик мог связать отчет об операциях клиента с хостом базы данных в считанные минуты, что позволяло быстро отлаживать проблемы.

Легкая масштабируемость: по мере того, как все больше команд регистрировались в Slack, мы могли просто предоставлять больше шардов и справляться с ростом.

НЕДОСТАТКИ

Ограничения масштабирования: в модели масштабирования было фундаментальное ограничение. Что, если какая-то команда и её данные не поместятся на самом большом шарде?

Привязка к одной модели данных: по мере роста мы запускали новые продукты, такие как Enterprise Grid и Slack Connect, которым не подходило хранение всех данных команды на одном шарде. Данная архитектура не только усложнила разработку этих фич, но и в некоторых случаях приводила к снижению производительности.

Горячие точки: мы не смогли распределить нагрузку крупных клиентов по всему серверному флоту, и в итоге получили несколько горячих точек. Поскольку было сложно предсказать использование Slack с течением времени, к большинству шардов была добавлена дополнительная емкость, что привело к простаиванию большой части ресурсов.

Влияние на критические функции: для всех основных функций (вход в систему, обмен сообщениями и присоединение к каналу) был необходим доступ к шарду, на котором находились данные команды. Когда шард испытывал сбой, каждый клиент, чьи данные были на этом шарде, был полностью отключен от сервиса. Нам нужна была архитектура, при которой недоступность второстепенного функционала не могла потенциально повлиять на критические функции, такие как отправка сообщений.

Дополнительная работа: в Slack использовалась нестандартная конфигурация MySQL, что потребовало написания значительного количество внутренних инструментов для возможности работать с этой конфигурацией при масштабировании. Кроме того, мы не могли нормально использовать реплики без переработки логики маршрутизации.

С самого начала мы задавались вопросом – совершенствовать существующий подход или заменить его?

Осенью 2016 года мы имели дело с сотнями тысяч MySQL запросов в секунду, разработчики регулярно сталкивались с проблемами масштабирования и производительности, а также с необходимостью разрабатывать обходные пути ограничений архитектуры. Нам нужен был новый подход к масштабированию и управлению базами данных.

Мы очень хотели продолжать использовать MySQL, т.к. в то время в приложении были тысячи различных запросов, некоторые из которых использовали специфичные для MySQL конструкции. Также у нас были годы накопленных практик для MySQL, связанных с развертыванием, резервным копированием, обеспечением надежности данных, ETL и многим другим. Уход от реляционной парадигмы (и даже от MySQL в частности) был бы весьма разрушительным изменением, поэтому мы исключили переход на NoSQL хранилища, такие как DynamoDB или Cassandra, а также NewSQL, такие как Spanner или CockroachDB.

Дополнительно хотелось бы отметить, что Slack весьма консервативен с точки зрения внедрения новых технологий, особенно для критически важных частей. В то время мы хотели и дальше посвящать большую часть энергии на разработку нового функционал и отдавали предпочтение более простым решениям касательно инфраструктуры.

Мы рассматривали вариант добавления шардинга по идентификатору канала и написали прототипы для более подробного изучения этой идеи. Стало ясно, что между логикой приложения и тем, как хранятся данные, уже существует довольно тесная связь и распутывание этой проблемы потребует много времени. Например, код выборки количества сообщений в канале зависел от команды, к которой принадлежал канал.

Примерно в это же время мы узнали о проекте Vitess. Vitess – система кластеризации баз данных для горизонтального масштабирования MySQL.

MySQL в основе: Vitess создан на базе MySQL. Использование MySQL гарантирует многолетний опыт разработчиков и надежность.

Масштабируемость: Vitess предоставляет сочетание многих важных функций MySQL с масштабируемостью NoSQL баз данных и позволяет гибко использовать шардинг базы данных без добавления логики в приложение.

Функциональность: Vitess автоматически обрабатывает такие функции, как переключение при отказе master и резервные копии. Отслеживает все метаданные о конфигурации вашего кластера, чтобы представление кластера всегда было актуальным и согласованным.

Расширяемость: Vitess – опенсорс проект, написанный на golang, с хорошим тестовым покрытием и процветающим сообществом разработчиков. Мы были уверены, что сможем внести все необходимые нам изменения (что мы и сделали).

Мы решили создать прототип, демонстрирующий, что мы cможем мигрировать данные из нашей традиционной архитектуры в Vitess и что Vitess оправдает ожидания. Внедрение нового хранилища данных для такого масштабного проекта как Slack задача не из легких, потребовалось приложить значительные усилия для создания новой инфраструктуры.

Нашей целью было построить вариант использования Vitess в продакшене для небольшого функционала: интеграции RSS-ленты в канал Slack. Для этого потребовалось переделать рабочие процессы для подготовки развертываний, обнаружения сервисов, резервного копирования/восстановления, управления топологией и многого другого. Нам также нужно было разработать новые интеграционные точки для перенаправления запросов в Vitess, универсальную систему клонирования существующих таблиц и систему сравнения, чтобы мы были уверены, что таблицы на базе Vitess имеют ту же семантику, что и наши легаси таблицы. Однако оно того стоило: приложение работало корректно, обладало гораздо лучшей производительностью, а эксплуатация и масштабирование кластера были проще. Не менее важно и то, что Vitess выполнил обещания по обеспечению отказоустойчивости и надежности. Данная миграция дала нам необходимую уверенность для продолжения инвестирования в проект.

Также мы выявили, что Vitess из коробки не будет работать для некоторых специфических потребностей Slack. Поскольку технология показала себя многообещающей в решении основных проблем, мы решили вложиться в разработку недостающего функционала. На сегодняшний день команда Slack является одним из крупнейших контрибьюторов в Vitness.

На графике показан прогресс миграции и несколько вех за последние несколько лет:

На данный момент у нас запущено множество кластеров Vitess с десятками пространств ключей (keyspaces) в разных географических регионах по всему миру. Vitess используется как нашим главным монолитом, так и другими сервисами. Каждое пространство ключей представляет собой логический набор данных, который масштабируется по количеству пользователей, команд и каналов.

В марте 2020 года мы наблюдали беспрецедентное увеличение использования Slack по мере того, как реальность пандемии COVID-19 поразила США, всего за одну неделю количество запросов выросло на 50%. Без переезда в Vitess мы бы просто не смогли масштабироваться для крупных клиентов и столкнулись бы с даунтаймом.

Благодаря выбору Vitess мы можем использовать одну и ту же технологию для всех новых сервисов Slack. Например, Vitess также является хранилищем для нашего продукта “Резидентство данных”, для которого работают кластеры в шести регионах. Использование Vitess здесь сыграло важную роль и позволило доставить этот продукт в рекордно короткие сроки, а команде разработчиков сосредоточиться на основной бизнес-логике.

Теперь, когда миграция завершена, мы с нетерпением ждем возможности использовать больше возможностей Vitess. Мы уже инвестировали в VReplication, одной из функций данного компонента является подписка на изменения, производимые на всех шардах, а также есть возможность материализованного представления данных.

На рисунке ниже показана упрощенная версия того, как выглядит развертывание Vitess в Slack.

Можно с уверенностью сказать, что Vitess был правильным выбором для Slack. Это не значит, что если бы Vitess не существовало, мы бы не придумали, как масштабировать хранилища данных. Скорее всего, с нашими требованиями мы бы пришли к очень похожему на Vitess решению. В некотором смысле эта история не только о том, как Slack масштабировал свои хранилища данных, но также о важности сотрудничества в нашей отрасли.