Сверхновые возможности ClickHouse

Сверхновые возможности ClickHouse

Случайный набор фич
о которых я вспомнил

Поддержка CONSTRAINTs

CREATE TABLE hits ( URL String, Domain String, CONSTRAINT c_valid_url CHECK isValidUTF8(URL), CONSTRAINT c_domain CHECK Domain = domain(URL) )

Данные проверяются при INSERT.

Разработчик — Глеб Новиков, ВШЭ

Поддержка CONSTRAINTs

INSERT INTO hits VALUES ('https://yandex.ru/', 'google.com') Exception: Constraint `c_domain` for table `hits` is violated at row 1. Expression: (Domain = domain(URL)). Column values: URL = 'https://yandex.ru/', Domain = 'google.com'.

Не проверяются при фоновых мержах.

Не проверяются при ALTER UPDATE.

Поддержка CONSTRAINTs

ALTER TABLE hits ADD CONSTRAINT c_valid_url CHECK isValidUTF8(URL)

При добавлении CONSTRAINT, старые данные не проверяются.

ALTER TABLE hits DROP CONSTRAINT c_valid_url

Параметризованные запросы

SELECT count() FROM test.hits WHERE CounterID = {id:UInt32} AND SearchPhrase = {phrase:String}

— подстановки именованные и типизированные;
— подставляются на уровне синтаксического дерева.

TODO:
— подстановки для множеств в секции IN;
— подстановки для имён таблиц, столбцов, баз данных.
— возможность задать URL с предустановленными запросами.

Разработчик — Александр Третьяков, ВШЭ

Параметризованные запросы

$ curl "http://localhost:8123/" --data-binary \ "SELECT count() FROM test.hits WHERE CounterID = {id:UInt32}" Exception: Query parameter `id` was not set. $ curl "http://localhost:8123/?param_id=34" --data-binary \ "SELECT count() FROM test.hits WHERE CounterID = {id:UInt32}" 1962 $ curl "http://localhost:8123/?param_id=34;%20DROP%20DATABASE%20test" ... "SELECT count() FROM test.hits WHERE CounterID = {id:UInt32}" Exception: Value 34; DROP DATABASE test cannot be parsed as UInt32 for query parameter 'id' $ curl "http://localhost:8123/?param_id=34;%20DROP%20DATABASE%20test" ... "SELECT count() FROM test.hits WHERE CounterID = {id:String}" Exception: Illegal types of arguments (UInt32, String) of function equals

Новые форматы

— Protobuf;

— Parquet;

ORC (input-only) — ArenaData;

Template, TemplateIgnoreSpaces;

Формат Template

Позволяет задать шаблон для форматирования или парсинга данных.

Шаблон содержит разделители и подстановки.

Для подстановок указан способ экранирования значений:
Quoted, Escaped, CSV, JSON, XML, Raw.

Website ${domain:Quoted} has ${count:Raw} pageviews.

Задаётся шаблон для строк, разделитель между строками
и шаблон для resultset.

Пример: распарсить access логи.
Пример: распарсить сложный вложенный JSON.
Пример: сформировать HTML прямо из ClickHouse.

Оптимизация запросов ORDER BY

с использованием ключа сортировки таблицы

SELECT event_time, message FROM system.text_log ORDER BY event_date DESC, event_time DESC LIMIT 10 ┌──────────event_time─┬─message───────────────────────────────────────────┐ │ 2019-09-05 04:20:35 │ Renaming temporary part tmp_insert_201909_10_10_0 │ │ 2019-09-05 04:20:35 │ Flushing system log │ │ 2019-09-05 04:20:34 │ Flushing system log │ │ 2019-09-05 04:20:34 │ Renaming temporary part tmp_insert_201909_10_10_0 │ │ 2019-09-05 04:20:28 │ Renaming temporary part tmp_insert_201909_9_9_0 to│ │ 2019-09-05 04:20:28 │ Flushing system log │ │ 2019-09-05 04:20:27 │ Renaming temporary part tmp_insert_201909_9_9_0 to│ ... └─────────────────────┴───────────────────────────────────────────────────┘ 10 rows in set. Elapsed: 0.291 sec. Processed 42.21 million rows, 6.10 GB (145.13 million rows/s., 20.98 GB/s.)

Оптимизация запросов ORDER BY

с использованием ключа сортировки таблицы

:) SET optimize_read_in_order = 1 :) SELECT event_time, message FROM system.text_log ORDER BY event_date DESC, event_time DESC LIMIT 10 ┌──────────event_time─┬─message───────────────────────────────────────────┐ │ 2019-09-05 04:28:59 │ Merged 6 parts: from 201909_1_96_19 to 201909_101_│ │ 2019-09-05 04:28:59 │ Renaming temporary part tmp_merge_201909_1_101_20 │ │ 2019-09-05 04:28:59 │ Merge sorted 761 rows, containing 202 columns (202│ │ 2019-09-05 04:28:59 │ Reading 2 marks from part 201909_101_101_0, total │ │ 2019-09-05 04:28:59 │ Reading 2 marks from part 201909_100_100_0, total │ ... └─────────────────────┴───────────────────────────────────────────────────┘ 10 rows in set. Elapsed: 0.012 sec. Processed 54.41 thousand rows, 7.42 MB (4.68 million rows/s., 637.76 MB/s.)

Оптимизация запросов ORDER BY

с использованием ключа сортировки таблицы

— ORDER BY по ключу сортировки;

— ASC / DESC / вперемешку:

ORDER BY event_date DESC, event_time ASC

— или его префиксу:

ORDER BY event_date DESC

— или по выражениям с монотонными функциями от него;

ORDER BY event_date DESC, -toUInt32(event_time)

— доступно с версии 19.14;

Разработчик — Антон Попов, Яндекс; Анастасия Родигина, ВШЭ

Заполнение пропусков в данных

SELECT EventDate, count() FROM table GROUP BY EventDate ORDER BY EventDate ┌──EventDate─┬─count()─┐ │ 2019-09-01 │ 5 │ │ 2019-09-02 │ 3 │ │ 2019-09-04 │ 4 │ │ 2019-09-05 │ 1 │ └────────────┴─────────┘

Заполнение пропусков в данных

SELECT EventDate, count() FROM table GROUP BY EventDate ORDER BY EventDate WITH FILL ┌──EventDate─┬─count()─┐ │ 2019-09-01 │ 5 │ │ 2019-09-02 │ 3 │ │ 2019-09-03 │ 0 │ │ 2019-09-04 │ 4 │ │ 2019-09-05 │ 1 │ └────────────┴─────────┘

Заполнение пропусков в данных

WITH FILL — модификатор для элемента ORDER BY;

WITH FILL FROM start

WITH FILL FROM start TO end

WITH FILL FROM start TO end STEP step

WITH FILL может быть применён к каждому элементу ORDER BY:

ORDER BY EventDate WITH FILL, EventTime WITH FILL STEP 3600

— ещё даже не помержено.

Разработчик — Антон Попов, Яндекс; Дмитрий Уткин, ВШЭ

Скрытие данных в логах

SELECT * FROM table WHERE Passport = 7609164285 <query_masking_rules> <rule> <regexp>Passport = \d+</regexp> <replace>Passport = ***</replace> </rule> <query_masking_rules>

— подавляет данные в логах и системных таблицах;

Разработчик — Михаил Филимонов, Altinity

Логи для интроспекции

в системных таблицах:

— system.query_log;

— system.query_thread_log;

— system.part_log;

— system.trace_log;

— system.text_log;

— system.metric_log;

Логи для интроспекции

system.text_log

Теперь мы пишем логи ClickHouse в ClickHouse!

DESCRIBE TABLE system.text_log ┌─name──────────┬─type───────────────────┐ │ event_date │ Date │ │ event_time │ DateTime │ │ microseconds │ UInt32 │ │ thread_name │ LowCardinality(String) │ │ thread_number │ UInt32 │ │ os_thread_id │ UInt32 │ │ level │ Enum8('Fatal' = 1, '...│ │ query_id │ String │ │ logger_name │ LowCardinality(String) │ │ message │ String │ │ revision │ UInt32 │ │ source_file │ LowCardinality(String) │ │ source_line │ UInt64 │ └───────────────┴────────────────────────┘

Логи для интроспекции

system.metric_log

— для тех, кто забыл настроить мониторинг.

— сохраняет все метрики ClickHouse раз в секунду (по-умолчанию).

SELECT toStartOfMinute(event_time) AS h, sum(ProfileEvent_UserTimeMicroseconds) AS user_time, bar(user_time, 0, 60000000, 80) AS bar FROM system.metric_log WHERE event_date = today() GROUP BY h ORDER BY h

Логи для интроспекции

SELECT toStartOfMinute(event_time) AS h, sum(ProfileEvent_UserTimeMicroseconds) AS user_time, bar(user_time, 0, 60000000, 80) AS bar FROM system.metric_log WHERE event_date = today() GROUP BY h ORDER BY h ┌───────────────────h─┬─user_time─┬─bar───────────────────────────────────────────────┐ │ 2019-09-05 04:12:00 │ 0 │ │ │ 2019-09-05 04:13:00 │ 0 │ │ │ 2019-09-05 04:14:00 │ 524000 │ ▋ │ │ 2019-09-05 04:15:00 │ 15880000 │ █████████████████████▏ │ │ 2019-09-05 04:19:00 │ 36724000 │ ████████████████████████████████████████████████▊ │ │ 2019-09-05 04:20:00 │ 17508000 │ ███████████████████████▎ │ │ 2019-09-05 04:21:00 │ 0 │ │ │ 2019-09-05 04:22:00 │ 0 │ │ │ 2019-09-05 04:23:00 │ 0 │ │ │ 2019-09-05 04:24:00 │ 0 │ │ │ 2019-09-05 04:25:00 │ 0 │ │ │ 2019-09-05 04:26:00 │ 0 │ │ │ 2019-09-05 04:27:00 │ 0 │ │ │ 2019-09-05 04:28:00 │ 0 │ │ │ 2019-09-05 04:29:00 │ 80000 │ │ │ 2019-09-05 04:30:00 │ 0 │ │ │ 2019-09-05 04:31:00 │ 0 │ │

Сэмплирующий профайлер запросов

Запоминает места в коде, в которых находилось выполнение запроса
в каждом потоке выполнения, в каждый момент времени с заданной периодичностью.

Если запрос тормозит — то каким местом?

— Где проводит время конкретный запрос?

— Где проводят время запросы некоторого вида?

— Где проводят время запросы конкретного пользователя?

— Где проводят время все запросы на кластере?

Разработчик — Никита Лапков, ВШЭ; и другие.

Сэмплирующий профайлер запросов

1. Выставляем одну или обе из этих настроек:

SET query_profiler_cpu_time_period_ns = 1000000; SET query_profiler_real_time_period_ns = 1000000;

2. Выполняем запросы.
Данные сохраняются в таблицу system.trace_log.

event_date: 2019-09-05 event_time: 2019-09-05 05:47:44 revision: 54425 timer_type: CPU thread_number: 149 query_id: b1d8e7f9-48d8-4cb3-a768-0a6683f6f061 trace: [140171472847748,61781958,110943821,117594728,117595220,115654933, 120321783,63251928,111161800,120329436,120331356,120308294,120313436,120319113, 120143313,115666412,120146905,111013972,118237176,111013972,117990912,111013972, 110986070,110986938,61896391,61897898,61887509,156206624,140171472807643]

Сэмплирующий профайлер запросов

trace — массив адресов в машинном коде (стек трейс);

Преобразовать адрес в имя функции:
— demangle(addressToSymbol(trace[1]))
Преобразовать адрес в имя файла и номер строки:
— addressToLine(trace[1])

* не забудьте установить clickhouse-common-static-dbg

Пример: топ функций:

SELECT count(), demangle(addressToSymbol(trace[1] AS addr)) AS symbol FROM system.trace_log WHERE event_date = today() GROUP BY symbol ORDER BY count() DESC LIMIT 10

Сэмплирующий профайлер запросов

Пример: топ функций:

┌─count()─┬─symbol──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ 517 │ void LZ4::(anonymous namespace)::decompressImpl<32ul, false>(char const*, char*, unsigned long) │ │ 480 │ void DB::deserializeBinarySSE2<4>(DB::PODArray<unsigned char, 4096ul, Allocator<false, false>, 15ul, 16ul>&, DB::PODArray<unsigned long, 4096ul, Allocator<false, false>, 15ul, 16ul>&, DB::ReadBuffer&, unsigned long) │ │ 457 │ DB::VolnitskyBase<true, true, DB::StringSearcher<true, true> >::search(unsigned char const*, unsigned long) const │ │ 270 │ read │ │ 163 │ void LZ4::(anonymous namespace)::decompressImpl<16ul, true>(char const*, char*, unsigned long) │ │ 130 │ void LZ4::(anonymous namespace)::decompressImpl<16ul, false>(char const*, char*, unsigned long) │ │ 58 │ CityHash_v1_0_2::CityHash128WithSeed(char const*, unsigned long, std::pair<unsigned long, unsigned long>) │ │ 44 │ void DB::deserializeBinarySSE2<2>(DB::PODArray<unsigned char, 4096ul, Allocator<false, false>, 15ul, 16ul>&, DB::PODArray<unsigned long, 4096ul, Allocator<false, false>, 15ul, 16ul>&, DB::ReadBuffer&, unsigned long) │ │ 37 │ void LZ4::(anonymous namespace)::decompressImpl<8ul, true>(char const*, char*, unsigned long) │ │ 32 │ memcpy │ └─────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Сэмплирующий профайлер запросов

Пример: топ контекстов выполнения для запроса:

SELECT count(), arrayStringConcat(arrayMap(x -> concat( demangle(addressToSymbol(x)), '\n ', addressToLine(x)), trace), '\n') AS sym FROM system.trace_log WHERE query_id = '1a1272b5-695a-4b17-966d-a1701b61b3eb' AND event_date = today() GROUP BY trace ORDER BY count() DESC LIMIT 10
count(): 154 sym: DB::VolnitskyBase<true, true, DB::StringSearcher<true, true> >::search(unsigned char const*, unsigned long) const /opt/milovidov/ClickHouse/build_gcc9/dbms/programs/clickhouse DB::MatchImpl<true, false>::vector_constant(DB::PODArray<unsigned char, 4096ul, Allocator<false, false>, 15ul, 16ul> const&, DB::PODArray<unsigned long, 4096ul, Allocator<false, false>, 15ul, 16ul> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, DB::PODArray<unsigned char, 4096ul, Allocator<false, false>, 15ul, 16ul>&) /opt/milovidov/ClickHouse/build_gcc9/dbms/programs/clickhouse DB::FunctionsStringSearch<DB::MatchImpl<true, false>, DB::NameLike>::executeImpl(DB::Block&, std::vector<unsigned long, std::allocator<unsigned long> > const&, unsigned long, unsigned long) /opt/milovidov/ClickHouse/build_gcc9/dbms/programs/clickhouse DB::PreparedFunctionImpl::execute(DB::Block&, std::vector<unsigned long, std::allocator<unsigned long> > const&, unsigned long, unsigned long, bool) /home/milovidov/ClickHouse/build_gcc9/../dbms/src/Functions/IFunction.cpp:464 DB::ExpressionAction::execute(DB::Block&, bool) const /usr/local/include/c++/9.1.0/bits/stl_vector.h:677 DB::ExpressionActions::execute(DB::Block&, bool) const /home/milovidov/ClickHouse/build_gcc9/../dbms/src/Interpreters/ExpressionActions.cpp:759 DB::FilterBlockInputStream::readImpl() /home/milovidov/ClickHouse/build_gcc9/../dbms/src/DataStreams/FilterBlockInputStream.cpp:84 DB::IBlockInputStream::read() /usr/local/include/c++/9.1.0/bits/stl_vector.h:108 DB::ExpressionBlockInputStream::readImpl() /home/milovidov/ClickHouse/build_gcc9/../dbms/src/DataStreams/ExpressionBlockInputStream.cpp:34 DB::IBlockInputStream::read() /usr/local/include/c++/9.1.0/bits/stl_vector.h:108 DB::ParallelInputsProcessor<DB::ParallelAggregatingBlockInputStream::Handler>::thread(std::shared_ptr<DB::ThreadGroupStatus>, unsigned long) /usr/local/include/c++/9.1.0/bits/atomic_base.h:419 ThreadFromGlobalPool::ThreadFromGlobalPool<void (DB::ParallelInputsProcessor<DB::ParallelAggregatingBlockInputStream::Handler>::*)(std::shared_ptr<DB::ThreadGroupStatus>, unsigned long), DB::ParallelInputsProcessor<DB::ParallelAggregatingBlockInputStream::Handler>*, std::shared_ptr<DB::ThreadGroupStatus>, unsigned long&>(void (DB::ParallelInputsProcessor<DB::ParallelAggregatingBlockInputStream::Handler>::*&&)(std::shared_ptr<DB::ThreadGroupStatus>, unsigned long), DB::ParallelInputsProcessor<DB::ParallelAggregatingBlockInputStream::Handler>*&&, std::shared_ptr<DB::ThreadGroupStatus>&&, unsigned long&)::{lambda()#1}::operator()() const /usr/local/include/c++/9.1.0/bits/shared_ptr_base.h:729 ThreadPoolImpl<std::thread>::worker(std::_List_iterator<std::thread>) /usr/local/include/c++/9.1.0/bits/atomic_base.h:551 execute_native_thread_routine /home/milovidov/ClickHouse/ci/workspace/gcc/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/unique_ptr.h:81 start_thread /lib/x86_64-linux-gnu/libpthread-2.27.so clone /build/glibc-OTsEL5/glibc-2.27/misc/../sysdeps/unix/sysv/linux/x86_64/clone.S:97

Сэмплирующий профайлер запросов

http://www.highload.ru/moscow/2019/abstracts/5835

Globs для File/URL/HDFS

SELECT * FROM file( 'logs*_{000..999}.tsv', TSV, 's String, x UInt8')

Множество файлов автоматически обрабатывается параллельно.

— ещё даже не помержено.

Правильные RPM пакеты

CentOS, RedHat... ГосЛинукс.

Поддерживаемые версии начиная от CentOS 5.

sudo yum install yum-utils sudo rpm --import \ https://repo.yandex.ru/clickhouse/CLICKHOUSE-KEY.GPG sudo yum-config-manager --add-repo \ https://repo.yandex.ru/clickhouse/rpm/stable/x86_64 sudo yum install clickhouse-server clickhouse-client

Ещё фичи

Изменение настроек таблиц налету

Функция neighbor

OS schedule priority

Table function VALUES

COLUMNS matcher

Live View

Ещё фичи

Каскадные Materialized Views

Нормальная логика AND/OR с NULL

Нормальная логика join_use_nulls

Правильный учёт оперативки запросами

Обновление DNS кэша

Settings constraints

Row Level security

Ещё фичи

Мутации не мешают мержам

Параллельная загрузка кусков

Оптимизация GROUP BY

Ускорение математических функций от Михаила Парахина

Ускорение функций для работы с JSON

Загрузка словарей не мешает друг другу

Оптимизация Merge таблиц

CREATE TABLE AS table_function()

А знаете ли вы...

— TTL для таблиц и отдельных столбцов;

— MySQL протокол;

— Adaptive granularity;

— Вторичные индексы;

— Функции текстового поиска;

— Функции для работы с JSON;

— WITH ROLLUP / WITH CUBE;

— ASOF JOIN;

Сентябрь 2019

— Поддержка гибридных хранилищ для свежих и старых данных;

— Создание и манипуляция словарями с помощью DDL запросов;

— Использование Z-Order для индексирования;

— Импорт и экспорт данных в S3;

— Параллельный парсинг форматов данных;

— Табличная функция input;

— Ускорение разбора VALUES с выражениями;

Октябрь 2019

— Начальная реализация RBAC;

— Начальная реализация Merge JOIN;

Осень-зима 2019

— Не начальная реализация RBAC;

— Не начальная реализация Merge JOIN;

— Workload management;

2020

Развитие Replicated таблиц:

— Wait-free ALTER ADD/DROP;
— ALTER RENAME COLUMN;
— полиморфные куски таблиц;
— поддержка мелких вставок;
— кворумный INSERT без линеаризуемости;
— INSERT без доступного ZooKeeper;
— отказ от хранения в ZooKeeper информации о кусках;
— альтернатива использованию ZooKeeper;
— VFS и хранение старых данных в S3;
— нормальная поддержка JBOD;
— эластичный кластер;

2020

— ALTER MATERIALIZED VIEW;
— оптимизация GROUP BY с учётом сортированности таблицы;
— функции обработки временных рядов;
— функции для статистических тестов;
— модификаторы DISTINCT и ORDER BY для всех агрегатных функций;
— прототип GPU offloading;
— пережатие старых данных в фоне;
— минимальная поддержка транзакций для множества вставок/чтений;
— реплицируемые базы данных;

2020, а вдруг повезёт

UDF на C++ и Rust.

Оконные функции.

Поддержка некоторых видов зависимых подзапросов.

.

.

Web site: https://clickhouse.com/

Maillist: [email protected]

YouTube: https://www.youtube.com/c/ClickHouseDB

Telegram chat: https://telegram.me/clickhouse_ru, clickhouse_en

GitHub: https://github.com/ClickHouse/ClickHouse/

Twitter: https://twitter.com/ClickHouseDB

Google groups: https://groups.google.com/forum/#!forum/clickhouse