Comments 56
Не подскажите какое количество одиночных вставок может выдержать buffer таблица? Не хотелось бы прибегать к batcher'ам, так как они внесут задержку.
На всякий случай, вдруг пригодится:
- мы будем прикручивать "входящую очередь" на основе MDBX к нашему Hiper100re (пока закрытая переделка ClickHouse).
- libmdbx спокойно выдерживает 10K-1000K вставок (зависит от размера записей).
- на диск конечно каждое изменение не персистится, но есть чекпоинты и ничего не теряется если не падает ядро ОС (и не выключается питание). Либо нужен writeback c батарейкой.
Внутри B+Tree без WAL, поэтому WAF имеет OlogN зависимость от объема данных. Но многое зависит от сценария использования:
- Если вставка идет в порядке возрастания ключей, то разница с RocksDB уже совсем не большая.
- Если допустимы потери при вылете ядра или отключении питания, то на порядок-другой быстрее.
- MDBX (но не LMDB) умеет LIFO_RECLAIM, что минимизирует набор горячих страниц/секторов, соответственно "writeback кеш с батарейкой" даёт на порядок больше.
Вероятно, это даёт наибольший вклад :).
Спасибо за разъяснение!
Камрады напомнили и попросили показать слайд сравнения "теплого с мягким" с прошлого Highload++:
Тем не менее, это данные реального теста. Если мне не изменяет память, то это результат make bench-quartet
на моем старом ноуте. Для воспроизведения достаточно собрать ioarena, а потом выполнить make bench-quartet
в поддиректории db/mdbx
.
В ближайший четверг (2018-11-29) буду с этой темой на Технологии управления данными от "Открытых Систем".
К нашему сожалению, удобных и простых в эксплуатации инструментов для чтения сырых данных
А чем не устроил JDBС драйвер и скажем DataGrip? Пробовал эту схему, все хорошо отображается и работает.
Вся консоль красная в итоге, — у меня очень много полей таких.
2. Nested( структуры плохо форматируются…
Наверное, логично было бы сделать вложенность какую-то.
3. Мой основной паттерн работы — это работа на кластере и мне совершенно неважно, какие там машины в кластере — то есть важно, но редко. То есть сейчас у меня 10 машин слева в списке вида ch******. А когда их будет 40? Кластер про шардирование и репликацию, а не про раздельное хранение разных данных.
В связи с этим меняется немного логика работы, — уже было бы приятно вести проект, иметь какой-то Project Explorer — Database Consoles всё же опять про то, что есть много серверов с разными таблицами, а не про 50 тачек в кластере, на которых просто создаются таблицы, альтерятся одновременно и т.д.
4. Говоря про ClickHouse, хотелось бы видеть всякие условные замеры размеров колонок, коэффициентов сжатия, — да и всей таблицы в целом. Да и опять же видеть это всё на кластере… Раз уж я вбил все сервера, то такая сущность как cluster, которая бы объединяла в себе эти сервера была бы удобна очень. До банального «запустить на кластере». Сейчас для того же «SET allow_experimental_low_cardinality_type = 1;» я выбираю галочками все машины (хорошо, что 1 раз).
P.S.
1. Чуток подтормаживает на запросах — потому я использую DataGrip скорее как некоторый «проект» для администрирования, чем как инструмент для работы с данными
2. В связи с этим или LightHouse или консоль — они быстрые, — там я и стараюсь крутить-вертеть данные.
3. Но если резюмировать, я в целом наслаждаюсь работой с DataGrip — это на мой вкус такой промежуточный вариант между какой-то полноценной средой для разработчика и консолью. Пользуюсь, лицензию купили :)
Запуск на кластере — пока только в том виде, как у нас есть. Скрипт запускаеш можно запустить на выбраных источниках данных.
Если у кластера есть единая точка входа, то присоединяться к ней. Если нет, то у нас пока только по datasource на каждую машину в кластере.
java.lang.RuntimeException: Parse exception: ByteFragment{[2018-12-10 18:23:48okayokay ip['1','2','3'][[1,2,3]]], start=47, len=9}
).
А Dbeaver рассматривали?
Бесплатен и поддерживает Clickhouse почти с момента появления.
А вот размер в *б — не знаю как
2. Как ищите по логам без ключевых полей (ip/id пользователя/что-то ещё)?
3. Какой объём данных в день и количество шардов, если не закрытая информация?
4. Если используете rsyslog, то почему не его протокол для приёма логов?
Пока что не используем Kubernetes
> 2. Как ищите по логам без ключевых полей (ip/id пользователя/что-то ещё)?
Обычно у нас структурированные логи, в которых все поля есть. Если их все же нет, то либо LIKE '%что-то%', либо с помощью visitParam* функций (так называются функции по работе с JSON в ClickHouse)
> 3. Какой объём данных в день и количество шардов, если не закрытая информация?
Любая информация, которую бы я привел, стала бы неактуальной очень быстро :). Конкретно видео логи занимают 1 шард (2 сервера), а всего хостов с ClickHouse несколько десятков, но эта цифра очень быстро растет.
2. То есть, у вас в строках, в полях message/content — json? И скорость работы поиска в таком случае устраивает?
3. Я больше спрашивал про дневной объём логов в гигабайтах, если не секрет (в сжатом виде в папке КХ)?
И мне кажется, что у вас не так много логов, если вы не натыкались на:
1. Нехватку памяти при работе с одной таблицей. У loghouse отдельные именно таблицы подневные/почасовые.
2. Медленным поиском по строкам. У loghouse кривоватый подход, но иначе в КХ и не получится. И в итоге данные можно заливать в уже структурированном виде. И поиск будет получше.
Возможно, я что-то неправильно понял в описании loghouse. У меня сложилось впечатление, что он заточен под kubernetes, которого у нас нет, соответственно для нас это не преимущество, а огромный недостаток. Если можно запустить отдельно в контейнере, то это хорошо.
> 2. То есть, у вас в строках, в полях message/content — json? И скорость работы поиска в таком случае устраивает?
Как я уже сказал, у нас структурированные логи и как правило мы ищем по конкретным полям, а не по строке. Если нам все-таки нужен поиск по строке, то производительность обычно устраивает :).
> 3. Я больше спрашивал про дневной объём логов в гигабайтах, если не секрет (в сжатом виде в папке КХ)?
Порядка 60-70 Гб в сутки, если речь про видео логи. При этом, нам часто нужно смотреть эти логи хотя бы за месяц, так что объем просматриваемых данных уже измеряется терабайтами :).
> 1. Нехватку памяти при работе с одной таблицей. У loghouse отдельные именно таблицы подневные/почасовые.
Неясно, откуда она должна возникнуть. У нас есть таблицы на 10-15 Тб и нам хватает памяти :).
> 2. Медленным поиском по строкам. У loghouse кривоватый подход, но иначе в КХ и не получится. И в итоге данные можно заливать в уже структурированном виде. И поиск будет получше.
Мы заливаем структурированные логи, так что такой проблемы не возникает.
Где у вас узкое место, ведь не в записи на диск же?
Вот тема с load average: если у вас много network latency — так это же хорошо, что load average большой — процессы сидят и ждут своего медленного сетевого io. Расшивают вам узкое место — сеть. Чем этих процессов «на подхвате» больше — тем больше bulk io и соответственно disk io. Ну конечно есть свои границы, но их нужно смотреть экспериментально. Тысячу-две процессов на машине с десятками ядер — это нормально.
Хадуп — это не быстро. Совсем. Аналитические колоночные БД вроде ClickHouse или Vertica в большинстве случаев быстрее на порядки
Мне сейчас Кафка кажется оптимальным вариантом — масштабируется почти под любой объем записи и далее разгребаем в удобном темпе и масштабе.
UDP позволяет не устанавливать соединения и просто слать запросы вне зависимости от количества клиентов (иначе бы пришлось устанавливать 100 воркеров * 10к хостов = 1 млн соединений, причем воркеры живут ограниченное время, так что соединения пришлось бы ещё постоянно переоткрывать).
Касаясь кафки, тут несколько важных соображений
- Из каждого воркера вставку по 1 записи кафка в любом случае не потянет, значит нужен прокси
- Некоторые логи мы хотим доставлять надёжно, даже если с кафкой проблемы, значит нам надо уметь писать на локальный диск машины
- Нет уверенности, не будет ли точно таких же проблем с потреблением памяти у кафки, если в нее пишут быстрее, чем она успевает писать на диск (кажется, что будут, если соединений десятки тысяч).
- Нам не очень удобен формат записи JSONEachRow или любой другой формат, в котором имело бы смысл писать в кафку (где нет понятия структуры таблицы)
- В чатик телеграма на момент принятия решения поступало много жалоб на реализацию Kafka engine в ClickHouse, что тоже не добавляло желания ее использовать
- У меня нет опыта работы с кафкой и это ещё одна точка отказа в цепочке, причем по которой лично у меня нет никакой экспертизы и нет никакого желания в ней разбираться и поддерживать ее работу
- Не все логи нам нужно доставлять надёжно. Большинство лучше наоборот дропать, если поток вставки в эту таблицу слишком большой (типичный пример – ошибки PHP или JS, начиная с некоторого объема нам уже не нужно больше писать в секунду :)). В системе с кафкой такого варианта не будет.
- Кафка удобна, если мы хотим делать какую-то батчевую обработку записей перед тем, как вставить их в ClickHouse, но у нас логи и мы их никак предварительно не обрабатываем, так что этого преимущества кафки тоже нет.
- Некоторых событий так много, что если хранить их в кафке всего одни сутки, места нам не хватит, или нужно будет добавить ещё примерно столько же серверов, сколько у нас сейчас выделено под ClickHouse, что звучит неразумно. (В ClickHouse эти данные занимают меньше благодаря сжатию и/или агрегации).
Наш внутренний RPC протокол основан на UDP, но умеет осуществлять перепосылку пакетов и умеет присылать подтверждения. Но поскольку ClickHouse не поддерживает UDP и нам иногда нужна надёжная доставка, при общении с ClickHouse мы используем только TCP и в нашем случае нам этого хватает. Но Kittenhouse поддерживает UDP в дополнение к TCP, и мы используем это при посылке логов из "труднодоступных" мест вроде nginx модулей. В этом случае у нас нет гарантии доставки.
3 Нет уверенности, не будет ли точно таких же проблем с потреблением памяти у кафки, если в нее пишут быстрее, чем она успевает писать на диск
9 Некоторых событий так много, что если хранить их в кафке всего одни сутки, места нам не хватит,
Я что то не могу найти ваши объемы, интересно сколько у вас в день логов и сколько занимает база в CH?
Входящий поток иногда может достигать нескольких гигабит/сек на одну машину. Это происходит редко, но когда происходит, мы должны эту нагрузку держать и не падать :). Про объемы написано в постановке задачи: нам иногда требуется хранить сотни терабайт.
Судя по всем тестам что я читал и личному оптыту это штатная ситуация для весьма скромных инсталляций кафки. Вот например events.static.linuxfound.org/sites/events/files/slides/HTKafka2.pdf
Там и бюджетный вариант и дорогой :)
Я и говорю — весьма скромных инсталляций кафки.
C одной стороны стони терабайт логов (и видимо тысячи серверов генерирующих их), а с дрогой нет ресурсов на лишний брокер кафки и одновременно с этим есть ресурсы на разработку и внедрения своего буфера перед CH.
Кафка из коробки умеет в батч (max.request.size, ...ms)
>Некоторые логи мы хотим доставлять надёжно, даже если с кафкой проблемы, значит нам надо уметь писать на локальный диск машины
Кафка из коробки умеет в кластер
>Нет уверенности, не будет ли точно таких же проблем с потреблением памяти у кафки, если в нее пишут быстрее, чем она успевает писать на диск (кажется, что будут, если соединений десятки тысяч).
Вопрос не в количестве соединений, а в общем размере сообщений.
>Нам не очень удобен формат записи JSONEachRow или любой другой формат, в котором имело бы смысл писать в кафку (где нет понятия структуры таблицы)
В кафку можно писать все, что угодно. Типизацию можно сделать в продьюсере и консьюмере
>В чатик телеграма на момент принятия решения поступало много жалоб на реализацию Kafka engine в ClickHouse, что тоже не добавляло желания ее использовать
После кафки можно/нужно поставить прокси
Еще в кафке есть дефлейт из коробки.
Это же настройка клиента на Java? У нас запросы обрабатываются в PHP. В PHP нет общего состояния между запросами, поэтому и батчить на стороне PHP не получится.
> Кафка из коробки умеет в кластер
Кластер решает лишь часть проблем. Например, мы можем в какой-то момент начать слать слишком много логов, и без записи на локальный диск мы можем в этом случае терять данные. Плюс бывают (редко) проблемы с сетью или фаерволами. Также обслуживание кластера усложняется — нет возможности его потушить целиком на какое-то время и произвести работы.
> Вопрос не в количестве соединений, а в общем размере сообщений.
Я не тестировал кафку на предмет того, как она будет себя вести, если мы будем активно в неё писать с десятков тысяч TCP/IP соединений. У меня есть подозрение, что могут возникнуть проблемы. Возможно, я не прав.
> В кафку можно писать все, что угодно. Типизацию можно сделать в продьюсере и консьюмере
У нас задача вполне конкретная — доставить (структурированные) логи до ClickHouse. Тот факт, что в кафку можно писать в любом формате в нашем случае это не плюс. Насколько я понимаю, есть 2 формата, которые поддерживаются при записи в Kafka:
1. JSONEachRow, в котором нельзя передавать бинарные данные (нам это нужно) и есть существенный оверхед на имена колонок
2. Cap'n'Proto, который эти проблемы решает, но мы этот формат нигде не используем, и из PHP в таком формате писать неудобно.
Ни один из этих форматов для нас не удобен.
> Еще в кафке есть дефлейт из коробки.
Интересно.
У нас запросы обрабатываются в PHP. В PHP нет общего состояния между запросами, поэтому и батчить на стороне PHP не получится.
Могу ошибаться, но в данном случае лучшая практик — из короткоживущих инстансов писать в stdout, а от туда собирать долгоживущим процессом и отправлять в ту же кафку.
Например, мы можем в какой-то момент начать слать слишком много логов,
Кафка прекрасно масштабируется и "слишком много" для неё скорее всего окажется также и "слишком много" для вообще любой системы. Астрономы, из ссылки что я давл ранее на 2-х машинах получают 1.3Gbps из 2 — 20 делаются довольно просто.
Также обслуживание кластера усложняется — нет возможности его потушить целиком на какое-то время и произвести работы.
А зачем тушить его целиком?
Прописываете репликацию у топика например 3 и можете тушить на обслуживание или апгрейд 2 из 3 брокеров на которых будут жить данные.
У нас задача вполне конкретная — доставить (структурированные) логи до ClickHouse. Тот факт, что в кафку можно писать в любом формате в нашем случае это не плюс.
Но ведь кафка всего лишь транспорт. Вас же не смущает отправка через UDP байт, вот и через кафку не должна смущать.
Насколько я понимаю, есть 2 формата, которые поддерживаются при записи в Kafka:
Это не так. Кафка поддерживает байты.
Разные клиентские библиотеки кроме простых байт или строк ещё предлагает разные варианты сериализации. Например (https://www.confluent.io/ из коробки поддерживает AVRO со схемами. Там и разные варианты сжатия в комплекте есть. У нас, например, неделя жизни банка (правда маленького) умещается в 35 гигов в том же AVRO
Разве блокировку на элемент надо ставить до снятия блокировки с контейнера? Код очень тяжело читается из-за этого — это оправдано?
github.com/VKCOM/kittenhouse/blob/f07cfa44a8d67bbfb7015fca1e459f7731ab27c0/core/inmem/buffer.go#L48
Как может появиться новый элемент вот тут? Читатели не блокирует друг друга, но писатель же блокирует читателей и эта ситуация невозможна или я заблуждаюсь?
github.com/VKCOM/kittenhouse/blob/f07cfa44a8d67bbfb7015fca1e459f7731ab27c0/core/inmem/buffer.go#L75
В целом. мне кажется проще и правильней было сделать так — писать напрямую в файл. При этом FS будет писать в буфер. В бэкграундтаске — раз в секунду или реже делать fsync — чтобы сбросить буфер на диск. А сейчас — вы по сути написали второй буфер — над файловой системой. Плюс я не смотрел код внимательно — но кажется синков -нет. Те вы пишете в буфер — потом в буфер файловой системы — во первых двойной буфер во вторых вы ничего не добились если не синкаете, разве нет? У меня очень похожая задача — массив файлов и запись в них — я решил ее напрямую и мне кажется так правильней: github.com/recoilme/pudge/blob/master/pudge.go
Да, потому что элемент удаляют из мапы вот тут:
github.com/VKCOM/kittenhouse/blob/f07cfa44a8d67bbfb7015fca1e459f7731ab27c0/core/inmem/flusher.go#L112
И потом сбрасывают его содержимое вот тут:
github.com/VKCOM/kittenhouse/blob/f07cfa44a8d67bbfb7015fca1e459f7731ab27c0/core/inmem/flusher.go#L53
Если сначала отпускать лок на мапу, а потом брать лок на элемент, то последняя запись в этот элемент может произойти после того, как содержимое буфера уже сбросили и выкинули.
Возможно, в этом месте это оверинжиниринг, то так точно не будет теряться запись в буфер.
> Как может появиться новый элемент вот тут? Читатели не блокирует друг друга, но писатель же блокирует читателей и эта ситуация невозможна или я заблуждаюсь?
Писатель блокирует читателей, однако писателей может быть несколько и между RUnlock() и Lock() может «вклиниться» другой писатель, который добавит элемент в мапу (или наоборот очистит её содержимое).
> В целом. мне кажется проще и правильней было сделать так — писать напрямую в файл.
Такое тоже есть. У kittenhouse 2 разных режима записи: в память и на диск. Во втором случае запись производится просто в файл и дальше отсылается содержимое из файла.
Запись: github.com/VKCOM/kittenhouse/blob/f07cfa44a8d67bbfb7015fca1e459f7731ab27c0/core/persist/log.go#L194
Чтение:
github.com/VKCOM/kittenhouse/blob/f07cfa44a8d67bbfb7015fca1e459f7731ab27c0/core/persist/send.go
> Плюс я не смотрел код внимательно — но кажется синков -нет.
Синков нет, всё правильно. Мы готовы пожертвовать тами данными, которые не сбросились на диск локальной машины в случае kernel panic или при потере питания (такое происходит довольно редко). Если добавить sync раз в секунду, то если мы будем писать в сотни файлов (что вполне возможно), мы будем создавать слишком большую нагрузку на локальный диск. Мы выбрали не делать sync вообще, но, в целом, нет причин, почему его нельзя добавить, как опцию.
https://github.com/tabixio/tabix
У нас есть общая таблица для логов, но там довольно мало данных пишется. Как правило, мы создаем отдельную таблицу под каждый вид логов. В этом случае полнотекстовый поиск не требуется и мы используем фильтрацию по конкретным полям.
Для сохранения порядка данных и отсутствия блокировок при чтении, можно использовать такую же схему (иммутабельные куски) как в MergeTree, но для данных в оперативке.
С помощью табличных настроек можно было бы управлять такими параметрами как: наличие или отсутствие WAL; частота применения fsync для WAL и необходимость его ожидания; возможность синхронно дождаться сброса буфера в основную таблицу.
Использование ClickHouse в VK, или Зачем мы написали KittenHouse