Как стать автором
Обновить
94
0
Алексей @o6CuFl2Q

Разработчик

Отправить сообщение
Авто-удаление исторических данных, скажем старше N дней (скажем 45 дней), как я понимаю в MergeTree данные партициируются за месяц и простого варианта нет? Или же создавать, таблицы одну на N дней и объединять их через MERGE и читать данные через неё, а если данные устарели удалять физические таблицы?

Да, простое удаление только по месяцам. Мы хотим когда-нибудь сделать custom-партиционирование.
По одной таблице на день — тоже вариант (у нас такое используется для одной задачи), но не стоит делать таких таблиц слишком много.
У нас внутри ClickHouse используется в качестве бэкенда к Graphite.

Для интеграции требуется отдельный код (патч для Graphite для чтения данных из ClickHouse; сервер для записи данных в ClickHouse пачками; для поиска имён метрик по шаблонам), который не выложен. Когда и как его выкладывать, будут решать авторы кода и их руководители, поэтому я пока ничего сказать не могу.

В самом ClickHouse есть domain-specific функциональность для поддержки Graphite — это GraphiteMergeTree. Посмотреть, что она делает, можно в исходниках.

Наверное, пока не выложена вся остальная «обвязка» над Graphite, самому пользоваться этим не стоит.
Этот пакет ещё не выложили (постараемся добавить). Можно собрать программу самому — программа расположена здесь.

На самом деле, документация в этом месте чуть-чуть устарела, так как у нас есть поддержка обычного HTTP сжатия (gzip/deflate через заголовки Accept-Encoding при чтении и Content-Encoding при отправке).

Как её включить, написано в примере здесь.

Единственная особенность — необходимость выставить параметр enable_http_compression в 1.
Это сделано для того, чтобы готовые HTTP клиенты не стали непреднамеренно использовать сжатие, которое может привести к повышенному потреблению CPU.

Также, при запросе можно указать настройку http_zlib_compression_level. По-умолчанию — 3.

Стоит иметь ввиду, что gzip сжимает медленно (на тесте time curl -sS 'http://localhost:8123/' -H 'Accept-Encoding: gzip' -d 'SELECT number FROM system.numbers LIMIT 10000000' | gzip -d | wc -c; получается всего 39 MB/sec.)
У меня нет ни лишнего времени, ни лишней сотни гигабайт на SSD, чтобы провести описанный эксперимент с ES. Буду рад, если вы это сделаете хотя бы с той парой запросов, что я написал. Ставлю 10 к 1, что ES будет быстрее раз в 5.

Надо попробовать, если удастся найти время.

In Elasticsearch, all data in every field is indexed by default.

В этом случае будет проблемой размер индексов.
Это нужно считать. Часто для данных в ClickHouse средний размер строки в сжатом виде получается очень маленьким. При среднем размере строки в десятки байт, размер индекса становится проблемой.

В ClickHouse для уменьшения объёма, специально используется «разреженный» индекс — он не позволяет найти, где расположена конкретная строчка, а позволяет найти только начало диапазона из index_granularity строк (рекомендуется 8192 для почти всех приложений). То есть, при чтении будет читаться немного лишних данных, зато индекс будет занимать примерно нулевое количество места по сравнению с объёмом данных.
Интересно! Жду доки, будет интересно огромную выборку попробовать.

Добавил в документацию.
Железо всё-таки секрет.

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

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

Если мы вдруг выясним, что можем обрабатывать запросы в два раза быстрее, то мы вместо этого предпочтём увеличить порог для срабатывания сэмплирования в Метрике — то есть увеличить объём данных для обработки и качество сервиса для пользователей.
Если всё работает со schema-less данными, то это отлично.
Schema-less не подходит, если необходимо любой ценой максимизировать скорость сканирования данных.
Это различие примерно аналогично различию между статической и динамической типизацией.

Для примера, ClickHouse может на простых запросах обрабатывать более миллиарда строк в секунду на одном сервере. А скорость в несколько сотен миллионов строк в секунду видна даже на примерах из этой статьи. И это вряд ли возможно со schema-less данными.

big flat точно не вариант, было бы огромное дублирование

Как ни странно, это как раз может быть нормальным. Дело в том, что повторяющиеся значения хорошо сожмутся. Хотя имеет смысл сделать несколько таблиц, но в самой детальной (events) дублировать столбцы из input, form и т. п.

Для примера, в данных Метрики, столбец Title — заголовок страницы — может очень часто повторяться для одного сайта (при этом он занимает в среднем 94 байта в несжатом виде). Но хранить Title inplace в таблице гораздо лучше, чем пытаться сохранить уникальные Title в отдельной таблице и делать сложный JOIN.
И есть ли какие-то известные проблемы c network partition?

При использовании ReplicatedMergeTree, в случае network partition, INSERT работает только в той части сети, из которой доступен кворум ZooKeeper. SELECT работает в обеих частях сети, но в одной из них будет возвращать устаревшие данные.

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

Кстати, говоря о консистентность, а какой уровень изоляции транзакции?

Транзакции не поддерживаются.
Запрос INSERT вставляет данные по блокам (не более 1 048 576 строк по-умолчанию). Вставка каждого блока атомарна.
(В случае таблиц семейства -MergeTree), запрос SELECT использует для выполнения snapshot данных, который захватывается на момент начала выполнения запроса.
Вы могли бы получить эти же данные по-другому: зайти на демо-счётчик в Метрике, открыть карту кликов; в браузере в отладчике открыть вкладку «сеть» и посмотреть, какие данные придут.

Результат
:) SELECT EventDate, Path, X, Y, T FROM mouse_clicks_layer SAMPLE 32768 WHERE CounterID = 29761725 AND URLNormalizedHash = 11352313218531117537 AND EventDate >= '2016-05-28' AND EventDate <= '2016-06-03' LIMIT 50

SELECT 
    EventDate, 
    Path, 
    X, 
    Y, 
    T
FROM mouse_clicks_layer
SAMPLE 32768
WHERE (CounterID = 29761725) AND (URLNormalizedHash = 11352313218531117537) AND (EventDate >= '2016-05-28') AND (EventDate <= '2016-06-03')
LIMIT 50

┌──EventDate─┬─Path───────────────┬─────X─┬─────Y─┬────T─┐
│ 2016-05-28 │ ?                  │ 15420 │ 32767 │   56 │
│ 2016-05-28 │ WA4AAAA1           │ 42802 │ 36408 │   73 │
│ 2016-05-28 │ ;A2AA4AAAA1        │ 29285 │ 56797 │   93 │
│ 2016-05-28 │ ;AAAA1AAA          │ 28086 │ 26623 │   33 │
│ 2016-05-28 │ ?                  │ 23130 │ 43007 │   19 │
│ 2016-05-28 │ ;A2AAAA1           │  4437 │ 35194 │  111 │
│ 2016-05-28 │ ;AAA3AAAAA1AA1     │ 26214 │ 26214 │  107 │
│ 2016-05-28 │ WA3AAAAA1AA1       │ 24341 │ 30246 │   91 │
│ 2016-05-28 │ ;A2AAAAA1AA1       │ 22469 │ 37808 │   44 │
│ 2016-05-28 │ ;AAA2AAAAA1AA1     │ 13107 │ 28398 │   35 │
│ 2016-05-28 │ A;A2AAAAA1AA1      │ 36044 │ 35388 │   25 │
│ 2016-05-28 │ WA5AAAAA1AA1       │ 18022 │ 31507 │   28 │
│ 2016-05-28 │ ;A3AA4AAAAA1AA1    │ 20830 │  4369 │   62 │
│ 2016-05-28 │ WA4AAAAA1AA1       │ 20596 │ 32767 │   47 │
│ 2016-05-28 │ PWWFA1AA1AAA       │ 18766 │ 45196 │   52 │
│ 2016-05-28 │ ?FA1AA1AAA         │ 38228 │ 10239 │  227 │
│ 2016-05-28 │ ;A1AAAAA1AA1       │ 37214 │ 23945 │  203 │
│ 2016-05-28 │ ;A1AAAAA1AA1       │ 25043 │ 49151 │   49 │
│ 2016-05-28 │ ;A1AAAAA1AA1       │ 15915 │ 21424 │   45 │
│ 2016-05-28 │ AA2AA1             │ 31302 │ 47645 │  163 │
│ 2016-05-28 │ WA3AAAAA1AA1       │ 22469 │ 26466 │   40 │
│ 2016-05-28 │ WA3AA3AAAAA1AA1    │ 32533 │ 13107 │   66 │
│ 2016-05-28 │ ;AAA3AAAAA1AA1     │ 35342 │ 45874 │   86 │
│ 2016-05-28 │ Qd1A1AA2AA1        │ 11179 │ 29490 │   98 │
│ 2016-05-28 │ ;A10AAAAA1AA1      │ 22703 │ 49151 │ 1068 │
│ 2016-05-28 │ WA5AAAAA1AA1       │ 38852 │ 24904 │   74 │
│ 2016-05-28 │ ;A2AA5AAAAA1AA1    │ 17085 │ 22366 │  158 │
│ 2016-05-28 │ AWA5AAAAA1AA1      │ 32767 │ 45874 │   81 │
│ 2016-05-28 │ A;A2AAAAA1AA1      │ 32767 │ 58981 │  135 │
│ 2016-05-28 │ AWA5AAAAA1AA1      │ 32767 │ 38010 │   66 │
│ 2016-05-28 │ AWA4AAAAA1AA1      │ 16383 │ 39321 │   96 │
│ 2016-05-28 │ AWA4AAAAA1AA1      │ 26214 │ 49151 │  112 │
│ 2016-05-28 │ A;A2AAAAA1AA1      │ 32767 │ 43253 │  122 │
│ 2016-05-28 │ AWA5AAAAA1AA1      │ 26214 │ 38010 │  150 │
│ 2016-05-28 │ AWA5AAAAA1AA1      │ 40631 │ 29490 │  172 │
│ 2016-05-28 │ AWA4AAAAA1AA1      │ 52428 │ 43253 │  178 │
│ 2016-05-28 │ A;A3AA4AAAAA1AA1   │ 36044 │ 50243 │  190 │
│ 2016-05-28 │ ;A8AA3AA4AAAAA1AA1 │ 10586 │ 23830 │  213 │
│ 2016-05-28 │ ;SA2AA2AA1         │ 52123 │ 23130 │   10 │
│ 2016-05-28 │ ;SA2AA2AA1         │ 53861 │ 30840 │   11 │
│ 2016-05-28 │ ;A2AAAAA1AA1       │ 31597 │ 35288 │   14 │
│ 2016-05-28 │ ;A7AA7AAAAA1AA1    │     0 │     0 │  722 │
│ 2016-05-28 │ ;Qd1A1AA2AA1       │ 16920 │ 10922 │ 1650 │
│ 2016-05-28 │ ;AAA2AA1           │ 55295 │ 43007 │  798 │
│ 2016-05-28 │ ;AAA2AA1           │ 40959 │ 36863 │  503 │
│ 2016-05-28 │ ;S3A2AA2AA1        │ 38859 │ 36408 │  275 │
│ 2016-05-28 │ ;AAA2AA1           │ 34815 │ 22527 │  133 │
│ 2016-05-28 │ ;A1AAAAA1AA1       │ 31831 │ 36548 │  112 │
│ 2016-05-28 │ dA2AA2AA1          │ 39648 │ 51881 │  903 │
│ 2016-05-28 │ WA4AAAAA1AA1       │  8659 │ 37808 │ 4198 │
└────────────┴────────────────────┴───────┴───────┴──────┘

50 rows in set. Elapsed: 0.032 sec.



Рассказывать, что это за строки (помогать разбирать работу счётчика Метрики) не буду.
Готового образа нет.
Но на Ubuntu 14.04 всё нормально ставиться несколькими командами, как в статье. В том числе, проверял на Amazon-е.
Сейчас проверил — получилось 25 мин. 24 сек. — это 109 336 строк в секунду.
Время включает разжатие xz и парсинг CSV (команда для загрузки — точно как написано в статье).
а на каких конфигурациях (железо, оси) проводились тесты?

Железо такое: двухсокетный E5-2650 v2, 128 GiB RAM, RAID-5 из 8 дисков SATA 7200 RPM на 6 ТБ.
ОС — Ubuntu 14.04.

И правильно ли я понял: *MergeTree не поддерживает доп. индексы (точней только secondary Date)?

Да, правильно. Для этого есть причины.

Пусть у нас есть индекс, и, при обработке запроса, согласно этому индексу, необходимо прочитать из таблицы множество данных по некоторому множеству смещений. Если множество достаточно большое (например, хотя бы от 1000 записей), и если данные не помещаются в оперативку, то при использовании HDD, скорость чтения этого множества будет определяться локальностью его расположения на диске. Если множество расположено не локально, то для его чтения, придётся выполнить примерно столько seek-ов, сколько элементов есть во множестве. Обычно это говорит о том, что если данные расположены недостаточно локально, то аналитические запросы не будут работать с приемлимой скоростью. Подробнее об этом рассказывалось в предыдущей статье.

Если порядок данных по двум индексам независим, и данные расположены в одном экземпляре, то мы можем добиться хорошей локальности лишь по одному индексу. Замечание: на самом деле, не совсем — см. эту информацию для размышлений.

Поэтому, локальность по нескольким индексам, если она нужна, имеет смысл достигать путём дублирования данных. Например, так устроены проекции в Vertica.

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

Я кстати не понял в чем радикальное отличие от Hive (если не считать скорости). Кстати как-то тюнили Hive или голый взяли?

Не тюнили. Более того, у нас тестировался довольно старый Hive, надо будет обновить.
Главное отличие — ClickHouse подходит для «онлайн» запросов из веб-интерфейса. То есть, скорость выполнения запросов позволяет выполнять запросы прямо в момент загрузки страницы с отчётом. Трудно представить себе сервис, в котором при каждом переходе между страницами, пользователь должен ждать выполнения запроса в Hive.

Да и ваш map reduce ограничен оперативкой, верно я понял?

Недавно была добавлена возможность внешней агрегации — с сохранением временных данных на диск. Эта возможность ещё не документирована.
Данные можно назвать сырыми, но не совсем.
В Метрике, в ClickHouse сохраняются неагрегированные (одна строка в базе — одно событие), но «чистые» и «обогащённые» данные.
То есть, перед загрузкой в ClickHouse, данные слегка обрабатываются (например, декодируются URL-ы); вычисляется и раскладывается в отдельные столбцы всё, что можно вычислить: поисковый запрос, регион, браузер и т. п.

Данные хранятся в таблице с первичным ключом.

Например, сейчас у нас в таблице hits, 8.2 триллиона строк. При каждой загрузке отчёта в интерфейсе Метрики (~2 млн. раз в сутки), делается запрос в ClickHouse. Ясно, что мы не могли бы показывать отчёты, если бы в таблице не было ключа.

Первичный ключ в таблицах Метрики начинается на идентификатор счётчика — то есть, клиента Метрики. Это позволяет быстро доставать данные по одному или по множеству счётчиков для отчёта.

При этом, также есть возможность выполнять глобальные запросы — по всем счётчикам сразу.
Для примера, запрос типа такого
SELECT CounterID, count() FROM hits_all WHERE EventDate = today() GROUP BY CounterID ORDER BY count() DESC LIMIT 20

Выполняется на кластере с такой скоростью:
20 rows in set. Elapsed: 0.815 sec. Processed 12.13 billion rows, 72.77 GB (14.87 billion rows/s., 89.25 GB/s.)
Пока нет, не планируем.

Так как у нас никто не использует Windows, обеспечить поддержку (автосборку, функциональные тесты, тесты производительности) под эту платформу нам довольно сложно.
Это весьма круто!
Спасибо за примеры.

одним запросом считающую 2 десятка показателей вроде квантилей среднего времени, потраченного на каждое поле, % юзеров, взаимодействовавших/менявших это поле, средней длины вводимого в поле текста и т.д. Все это с группировкой/фильтрацией по чему угодно — хоть по ОС или урлу, хоть по css-классу формы (и дате, естественно). В базе сырые данные — события (click, focusin, change и т.д.) и время

Вы прямо описываете sweet-spot сценарий для ClickHouse :)

все крутится на маленьком запасном сервере с HDD(!)

0.5 GB данных помещаются в page cache и поэтому запросы не должны упираться в дисковую подсистему.

С вашей штукой это превратилось бы в 30 отдельных запросов с кучей подзапросов и структуру данных из 3-4х таблиц, которые надо постоянно join'ить.

А может и не превратилось бы. Предположительно, будет одна «big flat table» с исходными событиями, и один или несколько запросов со всеми нужными агрегатными функциями. Возможно, с использованием такой функциональности.
На 10 млн. строк вряд ли время выполнения составит больше секунды.
Геораспределённость нужна нам для обеспечения аптайма Яндекс.Метрики при недоступности датацентра. Особенно в связи с «учениями».

Все перечисленные решения в сравнении плюс/минус тоже самое(кроме платных).

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

А вы не думали сделать аналог BigQuery на его основе? Мне кажется было бы интересно, тем более у вас имеются преимущества.

Не могу сказать.
Серьёзных причин делать именно так нет, но так оказалось довольно просто.
Так как вы не можете скачать этот сабмодуль, у вас будет пустая директория private.
При этом, всё собирается нормально, так как у всех собираемых программ нет зависимости от содержимого private.
Упоминание пустой директории в CMakeLists тоже нормально работает.

Если нет — можете указать версию CMake и привести пример, как он не работает.
Действительно, чаще говорят «колоночная».
Почему-то у нас закрепилось именно выражение «столбцовая». Как-то приятнее произносить.
Никакой разницы нет.
Это хорошо.

Вы не возражаете, если у меня будет к вам маленькая просьба?
Надо взять тестовые данные из этой статьи, загрузить их в Elasticsearch, и прогнать те запросы, которые приведены в качестве примеров.
Мне будет очень интересно посмотреть, как всё будет работать!

Информация

В рейтинге
Не участвует
Откуда
Россия
Работает в
Зарегистрирован
Активность