В прошлом году мы выпустили мажорную версию
своего продукта Solar Dozor 7. В новую версию нашей DLP-системы
вошел модуль продвинутого анализа поведения пользователей UBA. При
его создании мы попробовали разные базы данных, но по совокупности
критериев (о них скажем ниже) в итоге остановились на
ClickHouse.
Освоить ClickHouse местами было непросто, многое стало для нас
откровением, но главное преимущество этой СУБД затмевает все её
недостатки. Как вы поняли из заголовка, речь о скорости. По этому
параметру ClickHouse оставляет далеко позади традиционные
коммерческие базы данных, которые мы в своих продуктах, в том числе
в Solar Dozor, тоже используем.
В общем, если вы давно хотели, но никак не решались попробовать,
изучите наш опыт, он может вам пригодиться.
Кадры из мультфильма Турбо (2013 год)
О модуле UBA и его архитектуре
Принцип работы модуля UBA в том, что он анализирует активность
сотрудников в реальном времени, проверяя корпоративную почту, и
формирует личный профиль устойчивого поведения пользователя. В
случае отклонений Solar Dozor сообщает о них, чтобы можно было
вовремя заметить нехарактерные действия сотрудников.
Все данные, которые нужны UBA-модулю, лежат в ClickHouse. Его мы
ставим заказчику на ту же машину, куда инсталлируем и сам Solar
Dozor. В базе храним не письма, а метаданные сообщений, то есть
кто, когда, с кем переписывался, какова тема письма, какие вложения
оно содержит и т. д. Время хранения можно настраивать, нам для
расчетов нужен 90-дневный период. Поддержкой UBA-модуля занимаемся
мы, частично это могут делать и админы клиента. В любом случае
задача разработчиков максимально автоматизировать администрирование
БД.
Идем дальше. Основную работу делает UBA-сервер, который мы писали
на Clojure. Он обрабатывает данные, лежащие в ClickHouse, и
производит расчеты. UBA-сервер запоминает последнюю дату для
обработанных данных и периодически проверяет, есть ли в ClickHouse
более свежие данные, чем в расчете. Если есть, то старые результаты
удаляются и производится перерасчет.
Еще один компонент системы модуль Indexer, написанный на Scala. Он
умеет работать с разными источниками данных. В случае с UBA у
Indexer две задачи. Первая вытаскивать метаданные писем
пользователей из основной БД и отправлять их в ClickHouse. Вторая
выступать механизмом буферизации и грузить данные пачками. Когда
перейдем к подробностям работы с ClickHouse, я расскажу об этом
подробнее.
Для визуализации информации мы активно используем Grafana как при
разработке, так и при добавлении новых фич в готовый продукт.
Конечно, мы хотим перетащить все в свой интерфейс, но пока от этой
палочки-выручалочки отказаться сложно. На некоторые графики в
Grafana мы потратим пару часов, а разработчику понадобилась бы
неделя, чтобы сделать то же самое стандартными средствами. Обычно
это непозволительная роскошь. Поэтому, если дизайнеры не очень
сильно придираются к цвету графика или ширине надписи, Grafana это
спасение, ведь время у разработчиков расписано по минутам. Хотя это
дополнительный софт, и в случае с некоторыми нашими клиентами
иногда все упирается в сертификацию.
Сейчас вокруг ClickHouse появляются дополнительные средства
разработки. В частности, мы используем Metabase это удобное
средство прототипирования и быстрого моделирования расчетов.
Скорость. Я скорость!
Сначала несколько слов о том, какими критериями мы
руководствовались, выбирая СУБД для UBA-модуля. Нас
интересовали:
- стоимость лицензии;
- скорость обработки;
- минимальный объем хранения;
- скорость разработки;
- минимальное администрирование;
- минимальные требования к железу.
По первым четырем пунктам ClickHouse уверенно обошел конкурентов, и
мы остановились на нем. Поговорим о главном преимуществе
ClickHouse, ну, кроме того, что он бесплатный :-)
Скорость, скорость и еще раз скорость. Это главное, почему мы взяли
его на борт. С ClickHouse вы сможете обрабатывать миллион записей в
секунду на относительно скромных мощностях. Традиционные базы для
такого требуют куда более серьезного железа. Скорость работы
объясняется несколькими причинами:
- Принципиально иной метод хранения данных ClickHouse делает их
компрессию, архивирует и хранит по колонкам. Соответственно,
нагрузка на диск снижается.
- В ClickHouse распараллеливание задач на несколько потоков не
требует дополнительных усилий. СУБД использует все доступные
процессоры сервера без вмешательства админа. Если в случае с
традиционной базой для этого придется серьезно заморочиться, то
ClickHouse делает это по умолчанию. Нам, наоборот, приходится его
ограничивать, чтобы остальным сервисам что-то осталось.
- ClickHouse использует специальные инструкции процессора SSE,
AVX, благодаря чему быстро перелопачивает большие объемы данных в
оперативке. Тут логика простая: будучи созданным недавно,
ClickHouse рассчитан на новое железо и его новые возможности.
Благодаря этому UBA-модуль может быстро строить аналитику по
сообщениям пользователя и искать отклонения. В традиционных базах
данных несколько миллионов записей будут обрабатываться достаточно
долго, в ClickHouse при правильно написанном запросе это можно
сделать за секунды.
ClickHouse дает преимущества и на этапе разработки благодаря его
быстроте мы можем постоянно проводить эксперименты по обработке
данных и проверять гипотезы. И не тратим на это кучу времени.
Какую сборку выбрать
ClickHouse разработали в Яндексе изначально для своих нужд. Но
тогда собственные сборки они не выпускали. Это делала сторонняя
иностранная компания Altinity.
Многие наши заказчики российские компании, поэтому нам такой
вариант не очень подходил, и мы стали собирать ClickHouse сами.
Брали исходный код от Яндекса и генерили бинарные пакеты. Сказать,
что были сложности, значит, ничего не сказать. Приключений хватало,
на них тратилась куча ресурсов и времени. И при этом мы получали
утечку памяти, которой в сборках от Altinity не было. По мере
работы ClickHouse потреблял все больше памяти. В результате она
кончалась, и тот падал. Поэтому мы решили уйти от самостоятельной
сборки. Теперь проще есть бинарники от самого Яндекса, в крайнем
случае можно взять вариант от Altinity.
Обновления выходят довольно часто, но с ними нужно быть
осторожными. В каждой новой версии ClickHouse есть новые фишки,
исправления, но, как следствие, и новые баги. Разработчики Яндекса
не очень переживают, если новая версия может привести к проблемам с
совместимостью. Да, для нас это проблема, а они могут не
заморачиваться и добавлять новые функции, исходя из своих
потребностей и не тестируя все возможные сочетания функций. Но по
мере внедрения ClickHouse в большем количестве крупных компаний
отношение разработчиков Яндекса к клиентам постепенно
смягчается.
Изначально ClickHouse был NoSQL-СУБД, но теперь он понимает SQL.
Это тоже добавило нам немного проблем. Мы использовали прежний
вариант, а потом некоторые старые команды поменяли свой смысл.
(Оффтоп: лучше не забывать выносить код SQL-запросов из основного
кода приложения. Иначе потребуется доработка исходника при самых
тривиальных изменениях. Если этого не сделать на этапе разработки,
то в нашем случае при необходимости исправить тот или иной запрос у
клиента придется ждать выхода новой версии продукта).
Поэтому при обновлении мы берем недавнюю версию и начинаем на ней
тестироваться. И если ошибки возникают, мы можем поискать более
новую или более старую версию, где их не будет. Из-за этого процесс
затягивается. В любом случае тестируйте свой код при обновлении
ClickHouse, даже если оно минорное. Тут нужно соотносить выгоду от
новых фишек с рисками и с объемом тестирования.
Эгоист и альтруист одновременно
С традиционными базами данных история простая: настроили
использовать 20 Гб памяти они будут их использовать и никому не
отдадут. С ClickHouse все иначе. Резервировать память под него не
получится. Он будет использовать все, что найдет. Но и умеет
делиться ClickHouse отдает память, если в данный момент она ему не
нужна. С одной стороны, эта особенность позволяет нам развернуть на
одной машине несколько сервисов. А с другой стороны, нам приходится
ограничивать ClickHouse, так как другие модули Solar Dozor тоже
хотят кушать, а он делится памятью только тогда, когда самому не
надо. Поэтому прописываем такие параметры:
<max_memory_usage>
<max_memory_usage_for_user>
<max_memory_usage_for_all_queries>
<max_bytes_before_external_sort>
<max_bytes_before_external_group_by>
Число потоков
max_threads
также влияет на потребление.
Поэтому можно поколдовать и с этим параметром. Он определяет
параллельность работы ClickHouse. Если уменьшим ее в два раза, то и
потребление памяти при параллельных операциях тоже снизится в два
раза.
Как я уже сказал, обычно клиент выделяет нам под Solar Dozor одну
машину, на ней, кроме UBA, установлены и все остальные модули.
Поэтому у истории с бескорыстным ClickHouse есть и обратная
сторона. Другой софт может сожрать всю отданную память, и тому уже
ничего не достанется, придет OOM Killer. Конечно, было бы хорошо
резервировать под ClickHouse определенный объем памяти, но пока
такой функции нет.
О правильном секционировании и удачной сортировке
Мы секционировали табличку с метаданными сообщений (кто, кому,
когда, с какой темой, какие вложения, размер) по дням. Вообще, для
ClickHouse это очень мелкое дробление, но у нас большой объем
данных. Поэтому в наших условиях день это оптимальная единица и с
точки зрения структуры данных, и с точки зрения администрирования
системы.
Таблицу расчетов мы секционировали по месяцам. Понятно, что их
объем меньше, чем объем самих данных, поэтому тут агрегация по
месяцам удобнее.
Общее правило простое: не нужно делать слишком много секций, даже
если кажется, что ваш случай точно требует мелкого дробления
данных. Больше секций больше файлов на диске, а значит,
операционная система будет тупить. Если нам нужно что-то быстро
отобразить в интерфейсе, вытаскивать большой объем данных из одного
файла быстрее, чем из множества.
С сортировкой своя история. Данные столбца физически хранятся в
отдельном файле в сжатом виде в порядке, указанном в параметре
сортировки. При хорошем сжатии файл получается небольшим, и чтение
с диска занимает минимальное время. Если данные отсортировать
правильно, то одинаковые значения будут храниться рядом, а
алгоритмы компрессии ClickHouse хорошо сожмут их. Данные всех
столбцов должны быть физически отсортированы в одном и том же
порядке. Поэтому, если в таблице много столбцов, надо выбирать, по
какому набору столбцов нужно сделать сортировку. Если скомпоновать
и сжать эти данные неправильно, интерфейс или пакетная обработка
будут тормозить.
И несколько слов о запросах. Их условия должны содержать поле
секционирования. Оптимизатор запросов в ClickHouse пока далек от
того, что есть в других базах (например, PostgreSQL и Oraсle). Он
не понимает зависимости данных между столбцами. Чтобы
минимизировать объемы данных, считываемых с диска, нужно явно
указывать, какие диапазоны данных нужны для запроса. Условие
запроса для этого должно содержать границы данных по условию
секционирования. В идеале чтобы каждый запрос доставал данные из
одной секции, то есть указываем: сходи в конкретный день и ищи
только там.
К архитектурному решению, как мы будем разбивать и сжимать данные,
нужно отнестись ответственно. Потому что потом, когда у клиента уже
появляется какое-то количество записей в определенном формате,
что-то изменить очень сложно. Ведь данные хранятся в архивированном
виде, то есть, чтобы их перелопатить, нужна переупаковка.
Любитель есть большими порциями
Если до этого вы работали только с традиционными базами данных,
придется перестраиваться. С ClickHouse не прокатит каждый раз
вставлять по строчке: он не любит частых вставок. Если у нас много
источников данных и каждый вставляет по одной строчке, то
ClickHouse становится плохо. То есть, например, он выдерживает 100
вставок в секунду. Вы спросите: Как же так? 100 вставок и все?.. А
где же миллион в секунду, о котором говорили? Оказывается,
ClickHouse сделан так, что он может пережить 100 вставок в секунду,
а в каждой вставке при этом 10 000 строк. Вот вам и тот самый
миллион.
Вообще, для себя мы выработали правило: данные нужно вставлять не
чаще чем раз в 10 секунд. Так мы получаем необходимую скорость и
стабильную работу.
Но нужна промежуточная буферизация, которая накопит этот миллион.
Этим у нас как раз занимается Indexer, который я уже упоминал.
В Clickhouse есть такая фишка, как буферные таблицы, но это
полумера. С одной стороны, они позволяют волшебным образом
исправить ситуацию с частыми вставками без переделки серверного
кода. На нашем железе в буферную таблицу можно вставлять в 10 раз
чаще, чем в обычную. Но при этом память приходится распределять
вручную. Для каждой буферной таблицы нужно назначить заранее
определенное число и размер буферов. И использовать эту память под
что-то другое будет нельзя. Хорошо бы угадать с этими параметрами
сразу, иначе эти таблицы придется пересоздавать.
Поэтому самый правильный вариант буферизация в приложении, что мы и
сделали на этапе разработки. Несмотря на эти танцы с бубном, оно
того стоит: в традиционной базе данных вставить даже 1000 строк в
секунду, скорее всего, не получится даже на очень хорошем
железе.
Про мутации
В самых первых версиях ClickHouse не было команд изменения данных.
Можно было загрузить табличку и удалить. Потом добавили механизм
изменения данных, который называется мутацией. И это еще одна
штука, которую нужно понять и принять.
В традиционных базах данных вставил строчку, удалил строчку сразу
видишь результат. Тут все иначе. В ClickHouse пишешь команду
удаления, она ставится в очередь мутаций, и через некоторое время
будет выполнена. То есть по факту это выглядит так: вот данные
есть, и сейчас они еще есть, до сих пор есть, а теперь раз и
исчезли. Данные могут измениться в любой момент и по другой
причине: например, в результате оптимизации или дедупликации число
строк может уменьшиться. В ClickHouse нет физических первичных и
уникальных ключей. Поэтому в таблицах всегда есть дубли (а если
нет, то будут). Данные могут никогда не схлопнуться и/или никогда
не дедуплицироваться. Кстати, опция FINAL не работает, если есть
незаконченные мутации.
За скорость приходится платить дополнительными телодвижениями
разработчиков в коде надо предусмотреть обработку задержек на
выполнение мутации, чтобы пользователя это никак не коснулось. В
частности, нужно писать код (запросы, логику обработки данных) так,
чтобы не падать, если данные не соответствуют идеальному
представлению о них.
У нас исходные данные, которые хранятся в ClickHouse, не удаляются
и не меняются (за исключением добавления новых данных и удаления
старых по партициям). Меняются только расчеты. Мы пересчитываем
результаты при появлении новых данных, и у нас могут измениться
алгоритмы расчетов при установке новой версии. Все это не требует
построчных изменений в данных (операции update и delete). Поэтому в
нашем случае очень длинных очередей мутаций не бывает.
Не злоупотребляйте словарями
В ClickHouse есть такой интерфейс словари, куда можно положить
любую команду, которая вытащит список из какого-то источника.
Например, если нужно залезть во внешний файл со списком сотрудников
организации или в Active Directory.
Словари нужны для таких меняющихся данных, как должность, телефон,
семейное положение сотрудника и т. п. Если их корректное
отображение критично, то словарь необходим. Все, что не меняется,
нужно класть в обычные таблицы ClickHouse. Наиболее удобным для нас
оказался словарь в JSON, получаемый через http-запрос, самый
естественный и самый надежный. Пишем команду сходить на такой-то
сервер и взять то, что нам нужно.
В других БД все это сделать сложнее, надо писать специальную
программу, которая будет доставать списки из внешних источников. В
ClickHouse никаких дополнительных действий не нужно. Он достанет
данные из внешнего источника и будет работать с ними как с
таблицей.
Словари нужно создавать, поддерживать, дебажить. Но надо помнить,
что каждый словарь дополнительная зависимость от внешнего источника
и потенциальное место возникновения проблемы, если при запросе
окажется, что с источником что-то не так.
Как повысить надежность?
В отличие от традиционных баз данных ClickHouse не гарантирует
сохранность всех данных на все 100%. Вообще, для решения этой
задачи существуют специальные механизмы транзакций, откатов
изменений и восстановления после сбоев. В ClickHouse все это либо
не реализовано, либо сделано в минимальном объеме. Это тоже своего
рода плата за скорость. Впрочем, если сильно заморочиться, то можно
повысить надежность. Но придется строить кластер систему из
нескольких серверов, на которые мы установим ClickHouse и сервис
для распределенных систем ZooKeeper. Будем делать бэкапы,
репликацию данных. Очевидно, что это потребляет дополнительные
ресурсы, место на дисках, производительность и т. д.
Тут надо обратить внимание на три момента.
- Проектирование
Если спроектировать кластер неправильно, отказ любого компонента
может привести к отказу всего кластера. В каждом конкретном случае
выбор схемы будет разным. И нужно понимать, от каких аварий
конкретная схема защищает, а от каких нет. Но это тема для
отдельной статьи.
- Обслуживание
Все процедуры надо четко описать и протестировать. Ну и вообще, не
забывать про золотое правило: работает не трогай!
- Тестирование изменений на идентичном стенде
Любые изменения и обновления надо проверять не на уже работающей
системе, а на тестовой. Потому что, если что, смотри пункт 2.
В общем, средства повышения надежности совершенно точно понижают
производительность, а если накосячить, то и саму надежность.
Нам пока не приходилось сталкиваться с таким запросом от заказчика.
Но когда-то это наверняка произойдет, и мы готовимся к такому
варианту.
В любом случае тут все упирается в ваши возможности: стоимость
железа, сетевой инфраструктуры, обучения персонала и т. д. нужно
соизмерять с реальными потребностями в высокой доступности. Если
таких ресурсов нет, может быть, повышенная надежность не так уж и
нужна? Понятно, что ClickHouse история не про банковские или другие
критичные данные.
И напоследок: проверьте железо заранее
Казалось бы, железо без SSE 4.2 сейчас почти не встретить. Но
однажды нам достался мощнейший сервер с процессором, который эти
инструкции не поддерживает. А для ClickHouse поддержка SSE 4.2 одно
из системных требований. Оказалось, что закончился старый проект,
железо хорошее, не выбрасывать же.
Яндекс рекомендует выделять под ClickHouse отдельный сервер как
минимум с 30 Гб оперативки. В наших условиях никто не даст под БД
отдельное железо. Как я уже говорил, на одном сервере у заказчиков
крутится весь Solar Dozor со всеми его модулями и их компонентами.
Но, если все настроить правильно, полет пройдет нормально.
В общем, в нашем случае ClickHouse оказался хорошей альтернативой
традиционным СУБД. Ему не нужны серьезные мощности, чтобы
показывать впечатляющие результаты. ClickHouse делает более удобным
процесс разработки, но к нему нужно привыкнуть, подружиться с его
особенностями и реально оценивать уровень его возможностей.
Автор: Леонид Михайлов, ведущий инженер отдела проектирования
Solar Dozor