Предел PostgreSQL в AI-тестировании: переход на ClickHouse для обработки 20 млрд записей в день
Обработка заняла 21s
Как мы отказались от Postgres в пользу ClickHouse, чтобы обрабатывать 12 миллиардов записей кеша в день
От проблем с Postgres к скорости ClickHouse: рассказываем, как мы переработали архитектуру кеширования, чтобы выполнять более 2 млн запросов к кешу и обрабатывать 20 млрд записей в день, сохранив среднюю задержку разрешения около 250 мс.
Проблемы роста при использовании Postgres
Добавление новых значений в ключ кеша устранило многие проблемы с согласованностью данных, но одновременно увеличило количество активных записей кеша примерно с 80 тысяч до 1 миллиарда.
Изначально всё было просто: мы хранили кеш в одной таблице Postgres. Однако недостатки такого подхода проявились довольно быстро. Из-за высокой нагрузки на чтение и запись мы столкнулись как с повышенным потреблением ресурсов, так и с конкуренцией за блокировки между запросами, которые одновременно читали и изменяли кеш. Когда количество записей выросло на несколько порядков, ситуация стала ещё хуже.
Старая система — только Postgres.
Решение перейти на ClickHouse
Мы решили перенести хранилище в ClickHouse, чтобы повысить производительность запросов поиска по кешу. По мере роста Momentic такие запросы стали выполняться 600 тысяч раз в день.
Поскольку кеш — критически важная часть нашей инфраструктуры, нам требовалось обеспечить его стопроцентную доступность и задержку менее секунды. Теоретически для этого отлично подходил первичный ключ ClickHouse: по существу, у нас был только один запрос — выбрать записи кеша, соответствующие фильтрам по идентификатору теста, версии CLI, названию ветки и времени коммита.
Чтобы понять причины этого решения, важно разобраться в устройстве данных кеша Momentic. Ключ кеша состоит из:
идентификатора теста;
идентификатора шага;
версии Momentic;
ветки Git;
времени коммита.
Для заданных идентификатора теста, версии Momentic, ветки Git и коммита существует практически фиксированное количество значений — оно равно числу шагов теста и чаще всего составляет от 10 до 100.
Индексы Postgres основаны на B-деревьях, поэтому стоимость запроса неизбежно растёт вместе с объёмом данных. ClickHouse, напротив, использует разреженный первичный индекс. Если в каждом запросе нам известны четыре ключевых значения, мы можем очень эффективно сузить область поиска всего до нескольких гранул данных.
Как мы оптимизировали архитектуру ClickHouse
Первичный ключ ClickHouse решил 90% проблемы.
В ветках, отличных от main, мы могли легко определить часть данных, содержащую записи кеша для нужного теста, загрузить её в память и вычислить совпадающие записи. Обычно требовалось обработать от 10 до 30 тысяч строк.
Запросы в feature-ветке.
Однако с ветками main всё оказалось сложнее. Нам по-прежнему приходилось искать среди всех записей, время коммита которых предшествовало текущему. Иногда это означало просмотр более 500 тысяч строк.
В результате большинство запросов читало одну или две части данных, но некоторые выбросы затрагивали почти все части при каждом выполнении. Это вызывало резкие скачки потребления памяти и количества дисковых операций.
Надёжно обслуживать трафик стало сложно: производительность запросов сильно зависела от данных и характера использования конкретного клиента, а несколько неудачных запросов могли привести всю инфраструктуру в неуправляемое состояние.
Запросы в ветке main до оптимизации.
Чтобы решить проблему, мы создали материализованное представление, заранее вычисляющее все доступные времена коммитов для каждого идентификатора теста. Оно было значительно меньше основной таблицы и позволяло без труда определить оптимальный доступный коммит.
После этого мы искали в основной таблице только записи с конкретным временем коммита, снова сокращая область поиска до одной или двух частей данных.
Замена UPDATE на INSERT при продлении TTL
При использовании Postgres для каждого запуска теста мы выполняли три запроса:
SELECT — получить данные из кеша.
UPDATE — продлить TTL использованных записей кеша; частота обновлений ограничивалась с помощью Redis.
INSERT/UPDATE — сохранить обновлённые записи кеша.
Такой подход плохо сочетался с ClickHouse, поскольку потенциально два запроса из трёх были обновлениями, а ClickHouse выполняет их не особенно эффективно.
Вместо этого мы перешли исключительно на INSERT в сочетании с движком ClickHouse ReplacingMergeTree:
Выполняем SELECT, чтобы получить записи кеша.
Повторно вставляем использованные записи с помощью INSERT, тем самым продлевая их TTL.
После запуска теста вставляем новые записи.
ClickHouse асинхронно устраняет дубликаты.
Эффект оказался настолько значительным, что мы смогли полностью отказаться от слоя Redis. К тому моменту из-за возросшей кардинальности ключей кеша он и так приносил мало пользы.
Раньше: старая система на базе Postgres и Redis.
Теперь: новая система на базе ClickHouse.
Как мы мигрировали с Postgres на ClickHouse
Двойная запись
Чтобы во время миграции не потерять данные и проверить новый подход, сначала мы стали одновременно записывать кеш и в Postgres, и в ClickHouse.
Срок удаления устаревших записей кеша составлял 14 дней. Благодаря этому через две недели обе базы гарантированно содержали одинаковые значения.
Двойное чтение и проверка согласованности
Когда в обеих системах накопился одинаковый набор данных, мы начали проверять корректность и производительность запросов ClickHouse.
На этом этапе пользователи по-прежнему получали данные кеша из Postgres. Одновременно в фоновом режиме мы выполняли такой же запрос к ClickHouse, сравнивали результаты и отмечали все расхождения.
Это позволило нам:
Убедиться, что обе базы возвращают согласованные результаты.
Проверить производительность архитектуры ClickHouse и её способность работать в требуемом масштабе.
Именно на этом этапе мы смогли итеративно улучшить производительность, создав материализованное представление со временем коммитов.
Переключение на ClickHouse
Убедившись в надёжности конфигурации ClickHouse, мы начали постепенно переводить производственный трафик с Postgres на ClickHouse.
Двойную запись мы временно сохранили на случай, если потребуется откат. После первоначального периода проверки фоновая запись в Postgres была прекращена.
Новая система — только ClickHouse.
Результаты
Переход на ClickHouse позволил нам ежедневно выполнять более двух миллионов запросов к кешу и обрабатывать почти 20 миллиардов записей, сохраняя среднюю задержку разрешения около 250 мс.
Благодаря новой инфраструктуре мы смогли масштабно внедрить улучшения точности, описанные в предыдущей статье.
Количество запросов к кешу ClickHouse в день.
Задержка разрешения кеша в зависимости от конфигурации клиента.