Ограничение на размер таблицы.

5,727 views
Skip to first unread message

Alexey Churkin

unread,
Aug 8, 2016, 7:35:11 AM8/8/16
to ClickHouse
Добрый день.
Скажите, пожалуйста, есть ли ограничение на ширину таблицы? Зависит ли от ширины таблицы производительность запросов.
Мне хотелось бы иметь таблицу шириной от 800 до 16000 столбцов. При этом в основном для записей значения будут заданы для около 50-100 столбцов.

Рассчитан ли ClickHouse на такие широкие таблицы? Может быть для такой цели стоит использовать другую схему, чем для каждого свойства отдельная колонка?

Alexey Churkin

unread,
Aug 8, 2016, 7:45:21 AM8/8/16
to ClickHouse
Если более конкретно описать потребность - то это использование ClickHouse в DMP для получения точных объемов сегментов и их пересечения/объединения.

понедельник, 8 августа 2016 г., 14:35:11 UTC+3 пользователь Alexey Churkin написал:

man...@gmail.com

unread,
Aug 8, 2016, 3:00:24 PM8/8/16
to ClickHouse
Немного здесь написано: https://groups.google.com/d/msg/clickhouse/Ivj7BU6uNBU/IlehlZGcAQAJ

Явного ограничения нет. Таблица с 800 столбцов будет работать нормально.
Далее, чем больше столбцов, тем больше сложность INSERT и оверхед на один INSERT.
При использовании MergeTree, ClickHouse хранит каждый столбец в отдельном файле,
 и на каждый INSERT создаёт "кусок" данных со всеми столбцами (куски данных затем мерджатся в фоне).
Я думаю, что 10 000 столбцов будут работать довольно плохо, хотя это не проверялось.

Скорее всего, стоит делать по-другому.
Например, хранить в отдельной таблице множества идентификаторов посетителей для разных сегментов (SegmentID, UserID), и соединять с таблицей событий а также объединять/пересекать с помощью IN.

Alexey Churkin

unread,
Aug 8, 2016, 3:34:12 PM8/8/16
to ClickHouse
А насколько тяжелая операция для MergeTree добавления или удаления колонки? Быстро ли она выполняется? Зависит ли скорость выполнения такой команды от количества столбцов или строчек в таблице?


понедельник, 8 августа 2016 г., 22:00:24 UTC+3 пользователь man...@gmail.com написал:
Немного здесь написано: https://groups.google.com/d/msg/clickhouse/Ivj7BU6uNBU/IlehlZGcAQAJ

Явного ограничения нет. Таблица с 800 столбцов будет работать нормально.
Далее, чем больше столбцов, тем больше сложность INSERT и оверхед на один INSERT.

Возможно ли решить это batch загрузкой? Для моей задачи это вполне допустимо.
 
При использовании MergeTree, ClickHouse хранит каждый столбец в отдельном файле,
 и на каждый INSERT создаёт "кусок" данных со всеми столбцами (куски данных затем мерджатся в фоне).

Это так же работает, если я в INSERT-е записываю, например, 50 столбцов из 1000? Есть ли какая-то оптимизация для хранения значений по умолчанию для столбцов? Ну то есть, в среднем я планирую, что в одной строчке у меня будет около 50 столбцов иметь значение не по умолчанию.
 
Я думаю, что 10 000 столбцов будут работать довольно плохо, хотя это не проверялось.

Скорее всего, стоит делать по-другому.
Например, хранить в отдельной таблице множества идентификаторов посетителей для разных сегментов (SegmentID, UserID), и соединять с таблицей событий а также объединять/пересекать с помощью IN.

Не совсем понял схемы. Основные запросы, которые я планирую выполнять, это получить количество уникальных пользователей, которые, например, находятся в N определенных сегментов и при этом не находятся в K других сегментов. Сейчас для этого используются вероятностные структуры данных, но есть определенные ситуации, где они нас не устраивают.

man...@gmail.com

unread,
Aug 8, 2016, 4:06:32 PM8/8/16
to ClickHouse
Добавление и удаление столбца (ALTER) - дешёвые операции. При добавлении столбца ничего физически с данными не происходит: новые данные не записываются. При удалении столбца, происходит удаление файлов с данными столбцов.

При этом, ALTER таблицы прерывает и блокирует фоновые слияния а также передачу данных на реплики, также блокирует INSERT-ы и дожидается текущих INSERT-ов.
Поэтому выполнять ALTER-ы слишком часто не стоит.



Явного ограничения нет. Таблица с 800 столбцов будет работать нормально.
Далее, чем больше столбцов, тем больше сложность INSERT и оверхед на один INSERT.

Возможно ли решить это batch загрузкой? Для моей задачи это вполне допустимо.

Да, загрузка достаточно большими блоками поможет.
 
 
При использовании MergeTree, ClickHouse хранит каждый столбец в отдельном файле,
 и на каждый INSERT создаёт "кусок" данных со всеми столбцами (куски данных затем мерджатся в фоне).

Это так же работает, если я в INSERT-е записываю, например, 50 столбцов из 1000? Есть ли какая-то оптимизация для хранения значений по умолчанию для столбцов? Ну то есть, в среднем я планирую, что в одной строчке у меня будет около 50 столбцов иметь значение не по умолчанию.

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

 
 
Я думаю, что 10 000 столбцов будут работать довольно плохо, хотя это не проверялось.

Скорее всего, стоит делать по-другому.
Например, хранить в отдельной таблице множества идентификаторов посетителей для разных сегментов (SegmentID, UserID), и соединять с таблицей событий а также объединять/пересекать с помощью IN.

Не совсем понял схемы. Основные запросы, которые я планирую выполнять, это получить количество уникальных пользователей, которые, например, находятся в N определенных сегментов и при этом не находятся в K других сегментов. Сейчас для этого используются вероятностные структуры данных, но есть определенные ситуации, где они нас не устраивают.
 
Пишите

SELECT uniqExact(UserID) FROM table
WHERE SegmentID = 1
AND UserID IN (SELECT UserID FROM table WHERE SegmentID IN (2, 3))
AND UserID IN (SELECT UserID FROM table WHERE SegmentID IN (4, 5, 6))
...

В таблице сделайте первичный ключ, начинающийся с SegmentID.

Alexey Churkin

unread,
Aug 8, 2016, 4:19:24 PM8/8/16
to ClickHouse


понедельник, 8 августа 2016 г., 23:06:32 UTC+3 пользователь man...@gmail.com написал:
Добавление и удаление столбца (ALTER) - дешёвые операции. При добавлении столбца ничего физически с данными не происходит: новые данные не записываются. При удалении столбца, происходит удаление файлов с данными столбцов.

При этом, ALTER таблицы прерывает и блокирует фоновые слияния а также передачу данных на реплики, также блокирует INSERT-ы и дожидается текущих INSERT-ов.
Поэтому выполнять ALTER-ы слишком часто не стоит.


Явного ограничения нет. Таблица с 800 столбцов будет работать нормально.
Далее, чем больше столбцов, тем больше сложность INSERT и оверхед на один INSERT.

Возможно ли решить это batch загрузкой? Для моей задачи это вполне допустимо.

Да, загрузка достаточно большими блоками поможет.
 
 
При использовании MergeTree, ClickHouse хранит каждый столбец в отдельном файле,
 и на каждый INSERT создаёт "кусок" данных со всеми столбцами (куски данных затем мерджатся в фоне).

Это так же работает, если я в INSERT-е записываю, например, 50 столбцов из 1000? Есть ли какая-то оптимизация для хранения значений по умолчанию для столбцов? Ну то есть, в среднем я планирую, что в одной строчке у меня будет около 50 столбцов иметь значение не по умолчанию.

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

Про сжатие понял - это очень хорошо. Не понял, влияет ли это на производительность INSERT-ов? То есть вставка в широкую таблицу будет иметь одинаковые накладные расходы независимо от количества столбцов, что я вставляю? Ну то есть для таблицы в 1000 столбцов будет происходить запись в 1000 файлов даже если я вставляю только 20 столбцов?

 

 
 
Я думаю, что 10 000 столбцов будут работать довольно плохо, хотя это не проверялось.

Скорее всего, стоит делать по-другому.
Например, хранить в отдельной таблице множества идентификаторов посетителей для разных сегментов (SegmentID, UserID), и соединять с таблицей событий а также объединять/пересекать с помощью IN.

Не совсем понял схемы. Основные запросы, которые я планирую выполнять, это получить количество уникальных пользователей, которые, например, находятся в N определенных сегментов и при этом не находятся в K других сегментов. Сейчас для этого используются вероятностные структуры данных, но есть определенные ситуации, где они нас не устраивают.
 
Пишите

SELECT uniqExact(UserID) FROM table
WHERE SegmentID = 1
AND UserID IN (SELECT UserID FROM table WHERE SegmentID IN (2, 3))
AND UserID IN (SELECT UserID FROM table WHERE SegmentID IN (4, 5, 6))
...

В таблице сделайте первичный ключ, начинающийся с SegmentID.

Такой запрос будет эффективно работать в случае, если количество уникальных UserID будет несколько сотен миллионов, а количество уникальный пар (UserID, SegmentID) - несколько миллиардов? Или лучше подумать в сторону оптимизации и выполнять что-то вроде:

SELECT count() FROM table
WHERE (Segment_1 = 1 AND Segment_2 = 1 AND Segment_3 = 0) OR Segment_4 = 1

man...@gmail.com

unread,
Aug 8, 2016, 4:35:15 PM8/8/16
to ClickHouse

Про сжатие понял - это очень хорошо. Не понял, влияет ли это на производительность INSERT-ов? То есть вставка в широкую таблицу будет иметь одинаковые накладные расходы независимо от количества столбцов, что я вставляю? Ну то есть для таблицы в 1000 столбцов будет происходить запись в 1000 файлов даже если я вставляю только 20 столбцов?


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


Пишите

SELECT uniqExact(UserID) FROM table
WHERE SegmentID = 1
AND UserID IN (SELECT UserID FROM table WHERE SegmentID IN (2, 3))
AND UserID IN (SELECT UserID FROM table WHERE SegmentID IN (4, 5, 6))
...

В таблице сделайте первичный ключ, начинающийся с SegmentID.

Такой запрос будет эффективно работать в случае, если количество уникальных UserID будет несколько сотен миллионов, а количество уникальный пар (UserID, SegmentID) - несколько миллиардов? Или лучше подумать в сторону оптимизации и выполнять что-то вроде:

SELECT count() FROM table
WHERE (Segment_1 = 1 AND Segment_2 = 1 AND Segment_3 = 0) OR Segment_4 = 1
Ваш запрос, в котором сегменты в отдельных столбцах, будет работать лучше.
Но если действительно получится 10 000 столбцов, то вставка в таблицу может работать плохо, и придётся обратиться к варианту с меньшим количеством столбцов.
Есть вариант со столбцами типа UInt64, содержащими битовые маски.

Предложенный мной вариант (с одним столбцом SegmentID) может работать нормально, хотя надо уточнять.
Потребуется оперативка на все множества, образующиеся в результате подзапросов в секциях IN.
Её можно посчитать так: размер UserID (например, 8 байт в случае UInt64) умножить на 4 (верхняя оценка оверхеда хэш-таблицы). То есть, для миллиарда user-ов понадобится до 32 GB оперативки. Может быть, это неприемлимо.

Ещё вариант того же запроса:

SELECT count() FROM (SELECT UserID FROM table GROUP BY UserID HAVING max(SegmentID = 1) > 0 AND max(SegmentID = 2) + max(SegmentID = 3) > 0 ...)

А в варианте с отдельными столбцами, оперативка расходуется только на финальное множество user-ов, если вы используете uniqExact,
 либо расходуется только O(1) оперативки, если используется uniq или uniqCombined (у последнего погрешность в среднем < 0.1%).

Alexey Churkin

unread,
Aug 8, 2016, 4:38:56 PM8/8/16
to ClickHouse
Спасибо большое за консультацию. Получил ответы на все интересующие меня вопросы.

понедельник, 8 августа 2016 г., 23:35:15 UTC+3 пользователь man...@gmail.com написал:
Reply all
Reply to author
Forward
0 new messages