Обновить

Как оптимизировать хранение строк в ClickHouse

Помню как-то спорили с дата-инженерами, стоит ли использовать LowCardinality в DDL-запросах на создание объектов или это бесполезная фича и особого профита вообще не дает. С их стороны даже исследование какое-то было проведено. Как итог, LowCardinality стали использовать, но никто так и не смог наглядно показать в чем его преимущество.

На самом деле достаточно провести несколько тестов и все становится очевидно.

Создадим две таблицы. В одной тип данных определим как String во второй LowCardinality:

-- Таблица с обычным String
CREATE TABLE test_string (
    id UInt64,
    category String
) ENGINE = MergeTree()
ORDER BY id;

-- Таблица с LowCardinality
CREATE TABLE test_low_cardinality (
    id UInt64,
    category LowCardinality(String)
) ENGINE = MergeTree()
ORDER BY id;

Загружаем в каждую по 100 млн строк:

-- Заполняем первую таблицу (это займет пару секунд)
INSERT INTO test_string
SELECT 
    number AS id, 
    concat('category_name_', toString(number % 50)) AS category
FROM numbers(100000000);

-- Заполняем вторую таблицу такими же данными
INSERT INTO test_low_cardinality
SELECT 
    number AS id, 
    concat('category_name_', toString(number % 50)) AS category
FROM numbers(100000000);

Смотрим сколько данные занимают на диске:

SELECT 
    table,
    column,
    type,
    formatReadableSize(data_uncompressed_bytes) AS uncompressed_size,
    formatReadableSize(data_compressed_bytes) AS compressed_size_on_disk
FROM system.columns
WHERE table IN ('test_string', 'test_low_cardinality') 
  AND column = 'category'
ORDER BY table;

table               |column  |type                  |uncompressed_size|compressed_size_on_disk|
--------------------+--------+----------------------+-----------------+-----------------------+
test_low_cardinality|category|LowCardinality(String)|95.68 MiB        |750.46 KiB             |
test_string         |category|String                |1.56 GiB         |9.19 MiB               |

Можно заметить невооруженным глазом, что с LowCardinality данные на диске (compressed_size_on_disk) занимают в разы меньше места чем если бы мы просто хранили их в String. При распаковке данных (uncompressed_size) при чтении LowCardinality также сильно выигрывает. В оперативку будет загружено на порядок меньше данных, следовательно и сами запросы должны будут выполняться быстрее.

Проверим это на простых запросах на агрегацию:

-- Без LowCardinality
SELECT 
    category, 
    count() AS cnt
FROM test_string
GROUP BY category;

50 rows in result, 0.10 sec.
100.0%, Read 100.00 million rows, 2.38 GB
-- С LowCardinality
SELECT 
    category, 
    count() AS cnt
FROM test_low_cardinality
GROUP BY category;

50 rows in result, 0.02 sec.
100.0%, Read 100.00 million rows, 100.00 MB 
 

Запрос с LowCardinality выполнился в 5 раз быстрее и задействовал всего 100 MB RAM против 2.38 GB.

Вот и говорите потом, что LowCardinality не дает профита.

P.S. Главное правило: используйте LowCardinality только для полей с небольшим количеством уникальных значений (статусы, категории, типы). Для уникальных ID или URL он только навредит.

Ссылка на доку.

Мои статьи по ClickHouse на Хабре.

Теги:
+1
Комментарии0

Боты на серверах Telegram уже доступны — подайте заявку в бету

Хотите запустить Telegram-бота на инфраструктуре самого Telegram — без отдельного VPS, Docker-контейнера, настройки вебхуков и постоянного контроля за сервером? Похоже, функция постепенно становится доступной: участники сообщества @boto_shop уже написали, что получили бета-доступ к запуску ботов на серверах Telegram. Ниже инструкция, как подать заявку на доступ

Боты на серверах Telegram уже доступны — подайте заявку в бету

Публикации