Хабрахабр

Переход с Redshift на ClickHouse

Его выбрали потому, что на момент внедрения, по большому счёту, не было альтернатив, сравнимых по стоимости и удобству. Долгое время в iFunny использовался Redshift в качестве базы данных для событий, которые происходят в сервисах бэкенда и мобильных приложениях.

Мы долго его изучали, сравнивали стоимость, прикидывали примерную архитектуру и вот, наконец, этим летом решились посмотреть, насколько он нам полезен. Однако всё изменилось после публичного релиза ClickHouse. Из этой статьи вы узнаете о том, какую проблему нам помогал решить Redshift, и как мы перенесли это решение на ClickHouse.

Проблема

iFunny требовался сервис, похожий на Яндекс.Метрику, но исключительно для внутреннего потребления. Объясню почему.

Это могут быть мобильные приложения, веб-сайты или внутренние сервисы бэкенда. Внешние клиенты пишут события. Клиентов много, события они хотят отправлять постоянно и ждать совершенно не могут. Этим клиентам очень сложно объяснить, что сейчас сервис приёма событий недоступен, «попробуйте отправить минут через 15 или через час».

А большинство продуктовых метрик и результатов A/B-тестов вообще имеет смысл смотреть только раз в сутки, а может, и того реже. В противовес им есть внутренние сервисы и пользователи, которые достаточно толерантны в этом плане: они могут правильно работать даже с недоступным сервисом аналитики. В случае аварии или обновления мы можем себе позволить быть недоступными или неконсистентными по чтению несколько часов или даже дней (в особо запущенном случае). Поэтому требования к чтению достаточно низкие.

Если говорить о цифрах, то нам нужно принимать около пяти миллиардов событий (300 Гб сжатых данных) в сутки, при этом хранить данные за три месяца в «горячем», доступном для SQL-запросов виде, а в «холодном» — года за два или более, но чтобы в течение нескольких дней мы смогли их превратить в «горячие».

Типов событий около трёх сотен, каждый имеет свой набор свойств. В основном данные представляют собой набор событий, упорядоченных по времени. Ещё есть немного данных из сторонних источников, которые должны синхронизироваться с базой данных аналитики: например, коллекция инсталляций приложения из MongoDB или внешний сервис AppsFlyer.

Получается, что под базу данных нам требуется примерно 40 ТБ диска, а под «холодное» хранилище — ещё около 250 TБ.

Решение на Redshift

Данные принимает HTTP-сервис, проводит минимальную валидацию, собирает на локальном диске события в файлы, сгруппированные по одной минуте, сразу же сжимает и отправляет их в S3-бакет. Итак, есть мобильные клиенты и сервисы бэкенда, от которых нужно принимать события. Приложения не хранят состояния, поэтому легко балансируются, масштабируются и взаимозаменяются. Доступность этого сервиса зависит от доступности серверов с приложением и AWS S3. S3 относительно простой сервис хранения файлов, имеющий хорошую репутацию и доступность, поэтому на него можно положиться.

Тут всё достаточно просто: Redshift имеет встроенный S3-импортер, который является рекомендуемым способом загрузки данных. Дальше нужно как-то доставить данные в Redshift. Поэтому раз в 10 минут стартует скрипт, который подключается к Redshift и просит его загрузить данные по префиксу s3://events-bucket/main/year=2018/month=10/day=14/10_3*

А в случае проблем можно повторить загрузку за какие-то временные интервалы или загрузить «холодные» данные из S3 годичной давности. Для того чтобы отслеживать состояние задачи загрузки, мы используем Apache Airflow: он позволяет повторять операцию в случае ошибок и иметь наглядную историю выполнения, что важно при большом количестве таких задач.

SELECT ... В этом же Airflow точно так же, по расписанию, работают скрипты, которые подключаются к базе данных и выполняют периодические загрузки из внешних хранилищ, либо строят агрегации над событиями в виде INSERT INTO ...

Раз в неделю, на срок до получаса (в настройках указывается временное окно) AWS может остановить кластер на обновление или любые другие плановые работы. Redshift имеет слабые гарантии доступности. Обычно это занимает около 15 минут и случается примерно раз в полгода. При аварии на одной ноде кластер также становится недоступен до восстановления хоста. В текущей системе это не является проблемой, она изначально была рассчитана на то что база будет периодически недоступна.

8xlarge (36 CPU, 16 TБ HDD), что в сумме даёт нам 64 ТБ дискового пространства. Под Redshift использовалось 4 инстанса ds2.

Расписание резервных копий можно указать в настройках кластера, и работает оно прекрасно. Последний момент — это резервное копирование.

Мотивация перехода на ClickHouse

Конечно, если бы не было никаких проблем, никто бы и не подумал о миграции на ClickHouse. Однако, они были.

Обе базы данных являются колоночными, прекрасно работают с большим числом столбцов и очень хорошо сжимают данные на диске (а в Redshift можно настроить типы компрессии для каждой отдельной колонки). Если посмотреть на схему хранения ClickHouse с движком MergeTree и Redshift, то можно заметить, что их идеология очень похожа. Даже данные хранятся одинаково: они отсортированы по первичному ключу, что позволяет вычитывать только конкретные блоки и не держать отдельные индексы в памяти, а это важно при работе с большими объёмами данных.

Существенная разница, как всегда, в деталях.

Таблица на каждый день

Сортировка данных на диске и фактическое удаление в Redshift происходит в момент, когда вы выполняете:

VACUUM <tablename>

При этом процесс вакуума работает со всеми данными в этой таблице. Если в одной таблице хранить данные за все три месяца, этот процесс занимает неприличное количество времени, а выполнять его нужно хотя бы ежесуточно, потому как удаляются старые данные и добавляются новые. Приходилось строить отдельные таблицы на каждый день и объединять их через View, а это не только сложность в ротации и поддержке этой View, но и замедление запросов. При запросе, судя по explain, сканировались все таблицы. И хотя сканирование одной таблицы занимает меньше секунды, при их количестве в 90 штук получается, что любой запрос занимает как минимум минуту. Это не очень удобно.

Дубликаты

Следующая проблема — это дубликаты. Так или иначе, при передаче данных по сети есть два варианта: либо терять данные, либо получать дубликаты. Терять сообщения мы никак не могли, поэтому просто смирились с тем, что какой-то небольшой процент событий будет продублирован. Удалить дубликаты за сутки можно через создание новой таблицы, вставку в неё данных из старой, где с помощью оконной функции удалены строки с повторяющимися id, удалением старой таблицы и переименованием новой. Так как поверх дневных таблиц была view, нужно было про неё не забыть и удалить на время переименования таблиц. При этом нужно было следить ещё и за блокировками, иначе, в случае появления запроса, который блокировал view или одну из таблиц, этот процесс мог сильно затянуться.

Мониторинг и обслуживание

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

Стоимость

По нашим расчётам, развернуть ClickHouse на AWS-инстансах с точно такими же ресурсами получается ровно в два раза дешевле. Конечно, так и должно быть, ведь используя Redshift, вы получаете готовую базу данных, к которой сразу же после нажатия пары кнопок в консоли AWS можно подключиться любым клиентом PostgreSQL, а всё остальное AWS сделает за вас. Однако стоит ли оно того? У нас уже есть инфраструктура, мы вроде бы умеем делать бэкапы, мониторинг и конфигурацию, и делаем это для кучи внутренних сервисов. Почему бы не заняться поддержкой СlickHouse?

Процесс перехода

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

После пары недель тестов на небольшой копии данных, стало понятно, что для замены Redshift на Clickhouse придётся решить несколько вопросов:

  • на каких типах инстансов и дисках разворачивать;
  • использовать ли репликацию?
  • как установить, сконфигурировать и запустить;
  • как делать мониторинг;
  • какая именно будет схема;
  • как доставлять данные из S3;
  • как переписать все запросы со стандартного SQL на нестандартный?

Типы инстансов и диски. В количестве процессоров, диска и памяти решили отталкиваться от текущей инсталляции Redshift. Было несколько вариантов, в том числе i3-инстансы с локальными NVMe-дисками, но остановиться решили на r5.4xlarge и хранилище в виде 8T ST1 EBS на каждый инстанс. По подсчётам, это должно было дать сравнимую с Redshift производительность за вдвое меньшую стоимость. При этом за счёт использования EBS-дисков мы получаем простые резервные копии и восстановление через снапшоты дисков, почти как в Redshift.

Так как отталкивались от того, что уже сейчас есть в Redshift, решили не использовать репликацию. Репликация. К тому же это не заставляет сразу изучать ещё и ZooKeeper, которого в инфраструктуре пока нет, но отлично, что теперь появилась возможность сделать репликацию по требованию.

Это самая простая часть. Установка. Достаточно небольшой роли Ansible, которая установит уже готовые RPM-пакеты и сделает одинаковую конфигурацию на каждом хосте.

Для мониторинга всех сервисов используется Prometheus вместе с Telegraf и Grafana, поэтому просто поставили агентов Telegraf на хосты с ClickHouse, собрали в Grafana дашборд, где показывалась текущая загрузка серверов по процессору, памяти и дискам. Мониторинг. Получилось даже лучше и информативнее (и существенно быстрее), чем тот дашборд, что давала консоль AWS. Через плагин к Grafana вывели на этот дашборд текущие активные запросы по кластеру, состояние импорта из S3 и другие полезные вещи.

Одной из наших самых главных ошибок в Redshift было вынести в отдельные колонки только основные поля событий, а поля, которые редко используются, сложить
в одну большую колонку properties. Схема. А с другой стороны, запросы по большой колонке properties занимали всё больше и больше времени. С одной стороны, это дало нам гибкость в изменениях полей на начальных этапах, когда не было полного понимания, какие именно события мы будем собирать, с какими свойствами, к тому же они менялись по 5 раз за день. Получилась таблица примерно с двумя сотнями колонок. В ClickHouse мы решили сразу сделать правильно, поэтому собрали все возможные колонки и вписали им оптимальный тип.

Движок для хранения взяли ReplacingMergeTree, так как он позволяет удалять дубликаты из определённой партиции, просто выполнив OPTIMIZE… FINAL. Следующей задачей было выбрать правильный движок для хранения и партицирования.
С партицированием снова не стали думать, а сделали так же, как и было в Redshift, — по партиции на каждый день, но теперь все партиции являются одной таблицей, что
существенно ускоряет запросы и упрощает обслуживание. Кроме того, посуточная схема партицирования позволяет в случае ошибок или аварий, работать только с данными за сутки, а не месяц, что существенно быстрее.

Это был один из самых долгих процессов. Доставка данных из s3 в ClickHouse. Просто сделать загрузку встроенными средствами ClickHouse не получилось, потому что данные на S3 лежат в JSON, каждое поле нужно достать по своему jsonpath, как мы это делали в Redshift, а иногда ещё и применять трансформацию: например, UUID сообщения из стандартной записи в виде DD96C92F-3F4D-44C6-BCD3-E25EB26389E9 сконвертировать в байты и положить в тип FixedString(16).

Ничего готового не нашли, поэтому пришлось его сделать. Хотелось иметь специальный сервис, аналогичный тому, что мы имели в Redshift в виде команды COPY. Обращаться можно к любому из них. О том, как он работает, можно написать отдельную статью, но если вкратце, то это HTTP-сервис, который развёрнут на каждом хосте с ClickHouse. Сервер, к которому пришёл запрос, начинает сканировать файлы на S3 и раздавать работу по парсингу остальным хостам. В параметрах запроса указывается префикс S3, из которого забираются файлы, список jsonpath для преобразования из JSON в набор колонок, а также набор преобразований для каждой колонки. Это очень помогает расследовать проблемы и баги в сервисе приёма событий и клиентах, которые эти события генерируют. При этом нам важно, чтобы строки, которые не удалось импортировать, вместе с ошибкой складывались в отдельную таблицу СlickHouse. Конечно, если запросов станет больше, можно всегда вынести сервис импортёра на отдельные хосты. С размещением импортёра прямо на хостах базы данных, мы утилизировали те ресурсы, которые, как правило, простаивают, потому что сложные запросы к ним идут не круглосуточно.

В тех скриптах, которые были, просто изменили назначение с Redshift на ClickHouse. С импортом данных из внешних источников больших проблем не возникло.

К сожалению, он не подошёл, потому, что словарь обязательно должен помещаться в память, а размеры большинства коллекций в MongoDB этого не позволяют. Был вариант подключить MongoDB в виде словаря, а не делать ежесуточные копирования. Для этого используем layout ip_trie и CSV файлы, которые предоставляются сервисом. Но словари тоже нам пригодились: с их помощью очень удобно подключать GeoIP базы данных от MaxMind и использовать в запросах. Например, конфигурация словаря geoip_asn_blocks_ipv4 выглядит так:


<dictionaries> <dictionary> <name>geoip_asn_blocks_ipv4</name> <source> <file> <path>GeoLite2-ASN-Blocks-IPv4.csv</path> <format>CSVWithNames</format> </file> <\/source> <lifetime>300</lifetime> <layout> <ip_trie /> </layout> <structure> <key> <attribute> <name>prefix</name> <type>String</type> </attribute> </key> <attribute> <name>autonomous_system_number</name> <type>UInt32</type> <null_value>0</null_value> </attribute> <attribute> <name>autonomous_system_organization</name> <type>String</type> <null_value>?</null_value> </attribute> </structure> </dictionary>
</dictionaries>

Этот конфиг достаточно положить в /etc/clickhouse-server/geoip_asn_blocks_ipv4_dictionary.xml, после чего можно делать запросы к словарю, чтобы получить название провайдера по IP адресу:

SELECT dictGetString('geoip_asn_blocks_ipv4', 'autonomous_system_organization', tuple(IPv4StringToNum('192.168.1.1')));

Изменение схемы данных. Как упоминалось выше, решили пока не использовать репликацию, так как сейчас можем себе позволить стать недоступными при авариях или запланированных работах, а копия данных уже лежит на s3 и мы можем перенести ее в ClickHouse за разумные сроки. Если нет репликации, то и ZooKeeper разворачивать не стали, а отсутствие ZooKeeper ведет и к невозможности использовать выражение ON CLUSTER в DDL запросах. Эта проблема решилась небольшим скриптом на python, который подключается к каждому хосту ClickHouse (пока их всего восемь) и выполняет указанный SQL-запрос.

Процесс перекладывания запросов из синтаксиса Redshift в синтаксис ClickHouse шёл параллельно с разработкой импортёра, и занималась им в основном команда аналитиков. Не полная поддержка SQL в ClickHouse. Чтобы понять, как их можно сделать через массивы и лямбда-функции, ушло несколько дней. Как это ни странно, но дело оказалось даже не в JOIN, а в оконных функциях. В этот момент данные уже писались сразу в два места: и в Redshift, и в новый ClickHouse, поэтому при переносе запросов мы сравнивали результаты. Хорошо, что этот вопрос достаточно часто освещается в докладах про ClickHouse, которых огромное количество, например, events.yandex.ru/lib/talks/5420. Сравнить скорость было проблематично, так как мы убрали одну большую колонку properties, и большинство запросов стало работать только с нужными колонками, что, естественно, дало существенный прирост, но те запросы, где колонка properties не участвовала, работали так же, либо чуть быстрее.

В итоге получилась такая схема:

Результаты

В сухом остатке мы получили следующие преимущества:

  • Одна таблица вместо 90
  • Служебные запросы исполняются за миллисекунды
  • Стоимость уменьшилась в два раза
  • Простое удаление дубликатов событий

Есть и недостатки, к которым мы готовы:

  • В случае аварии, чинить кластер придётся самостоятельно
  • Изменения схемы теперь нужно производить на каждом хосте отдельно
  • Обновляться на новые версии придется своими силами

Сравнивать скорость запросов в лоб мы не можем, так как существенно изменилась схема данных. Многие запросы стали быстрее, просто из-за того что читают меньший объём данных с диска. По-хорошему, такое изменение нужно было сделать ещё в Redshift, но было решено совместить с миграцией на ClickHouse.

Шла она с начала июля по конец сентября и потребовала участия двух человек. Вся миграция вместе с подготовкой заняла около трёх месяцев. Получается, уже чуть более двух месяцев. 27-го сентября, мы выключили Redshift и с тех пор работаем только на ClickHouse. Впереди нас ждут обновления на новые версии! Срок небольшой, но пока ни разу не натолкнулись на потерю данных или критичный баг, из-за которого весь кластер бы встал.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть