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

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

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

  • По мере того, как наша интеграция данных и количество клиентов росли, объем данных, которые нам нужно было принимать, также увеличивался в несколько раз.
  • Теперь, когда наши клиенты находились в разных географических регионах, мы больше не могли запускать задания в определенное время.
  • Теперь у нас есть сотни рабочих процессов в нашем диспетчере данных (наш внутренний инструмент оркестровки инфраструктуры). Наши задания выполняются из-за различных триггеров, некоторые из которых запускаются клиентом, некоторые нашей внутренней командой, а некоторые запускаются cron. Мы можем контролировать параллелизм каждого задания индивидуально, но было сложно контролировать количество одновременно выполняемых заданий и степень воздействия, которое они могли бы оказать на базу данных в любой момент времени.

Все эти проблемы проявлялись во временных скачках нагрузки и времени отклика API. Итак, что вы можете сделать, чтобы предотвратить возникновение этой проблемы? Это два возможных решения, которые мы пробовали.

  1. Маршрутизация наших запросов чтения на выделенную реплику чтения.
  2. Управление нагрузкой / регулирование.

Решение № 1. Реплика чтения
Маршрутизируя запросы чтения от ваших приложений к реплике чтения, вы можете снизить нагрузку на свой главный экземпляр БД. Используя реплики чтения, вы можете горизонтально масштабировать рабочие нагрузки базы данных с большим количеством операций чтения, выходящие за рамки ограничений емкости одного экземпляра БД.

Однако поддержание низкого уровня задержки реплики является огромной задачей само по себе, задержка реплики - это количество времени, в течение которого ваше ведомое устройство отстает от вашего главного экземпляра. Поскольку репликация выполняется в одном потоке, то, что может показаться не слишком большой нагрузкой для вашего могущественного мастера, может оказаться слишком большой для вашей реплики.

Мы видели случаи, когда некоторые излишне нетерпеливые пакетные задания, выполняющие обновления из 10 потоков на главном сервере, увеличивали нашу задержку репликации до часов, а не минут. Это решение не помогло нам.

Решение №2: управление нагрузкой / регулирование

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

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

Свойства группы ресурсов:

  • RESOURCE_GROUP_NAME - имя группы ресурсов
  • VCPU_IDS - значения показывают диапазон, включающий все доступные процессоры. Время ЦП - это управляемый ресурс, представленный концепцией «виртуальный ЦП» как термин, который включает ядра ЦП, гиперпотоки, аппаратные потоки и т. Д.
  • THREAD_PRIORITY - приоритет потока - это приоритет выполнения для потоков, назначенных группе ресурсов. Диапазон значений приоритета от -20 (высший приоритет) до 19 (низший приоритет). Приоритет по умолчанию равен 0 как для системы, так и для групп пользователей. Системным группам разрешен более высокий приоритет, чем группам пользователей, гарантируя, что пользовательские потоки никогда не будут иметь более высокий приоритет, чем системные потоки: для групп системных ресурсов разрешенный диапазон приоритета составляет от -20 до 0. Для групп ресурсов пользователей разрешенный диапазон приоритета равен 0 до 19.
  • RESOURCE_GROUP_TYPE («система» или «пользователь») - это может быть SYSTEM или USER. Тип группы ресурсов влияет на диапазон значений приоритета потока ОС, назначаемых группе.
  • RESOURCE_GROUP_ENABLED (1 или 0) - группы могут быть включены или отключены, чтобы управлять возможностью назначения им потоков.

Мы используем Amazon RDS с ограничением, если не установлена ​​возможность CAP_SYS_NICE, приоритеты потоков групп ресурсов в Linux игнорируются. Поскольку мы используем amazon rds, мы не могли установить возможность CAP_SYS_NICE, поскольку у нас нет доступа к базовому инстансу EC2, следовательно, в настоящее время в Amazon RDS вы не можете установить приоритет потока. Если у вас есть собственная установка MySQL, подробнее о настройке CAP_SYS_NICE здесь.

Кроме того, в Amazon RDS по умолчанию суперпользователь не имеет разрешений RESOURCE_GROUP_ADMIN и RESOURCE_GROUP_USER, для создания группы ресурсов требуется разрешение администратора, а для назначения группы ресурсов потоку требуется разрешение пользователя. Итак, предоставьте эти разрешения вашему пользователю.

GRANT RESOURCE_GROUP_ADMIN, RESOURCE_GROUP_USER ON *.* TO '<username>'@'%' WITH GRANT OPTION;

После установки разрешений вы сможете увидеть группы безопасности по умолчанию.

SELECT * FROM INFORMATION_SCHEMA.RESOURCE_GROUPS;

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

Например, если в базе данных 8 ядер, диапазон VCPU_ID будет от 0 до 7. Мы не можем установить приоритет потока, следовательно, приоритет потока всегда равен 0.

Создать новую группу ресурсов.

CREATE RESOURCE GROUP ETL
TYPE = USER
VCPU = 0-2            -- out of 8 cores we are assigning 2 to ETL
THREAD_PRIORITY = 0;  -- does not matter it would be set to 0

Как интегрировать его в свой код после того, как вы определили группы ресурсов в базе данных.

  1. Подсказка по запросу: для выполнения запроса SQL он переключит данный поток на группу ресурсов «ETL», а затем переключит его обратно после завершения выполнения запроса.
Select RESOURCE_GROUP(ETL) column from table

2. Установка группы ресурсов для текущего подключения.

SET RESOURCE GROUP 'ETL'

Поскольку у нас много кодовых баз, а ORM сейчас это не поддерживают, нам было проще использовать второй метод.

Большинство наших проектов основано на Rails и Sidekiq, поэтому мы исправили класс AbstractMysqlAdapter ActiveRecord, который позволяет нам устанавливать группу ресурсов при каждом новом подключении в наших пакетных заданиях.

Разница огромная!

Теперь наш пользовательский интерфейс не ухудшается неожиданно, даже когда одновременно выполняется много заданий. И теперь, чтобы предсказуемо контролировать скорость приема данных, у нас есть центральная ручка (количество процессоров, определенных в нашей группе ресурсов ETL), которую мы можем настраивать.

Если вас интересуют такие оптимизации. Присоединяйтесь к нам.