почитать, как добиться высокого QPS с хорошим временем выполнения запроса и не утилизировать CPU до 100%.
Есть нетривиальная задача: Таблица collapsingMT необходимо сделать достаточно быструю вставку, а также быструю выборку.
В текущем варианте таблица выглядит так:
CREATE TABLE default.tab1
(
`id` UInt64,
`vid` UInt8,
`stat` UInt8,
`type` UInt32,
`customer_id` UInt32,
`param` Decimal(15, 5),
`id_se` UInt64,
`value1` Decimal(16, 2),
`value2` Decimal(16, 2),
`sign` Int8,
`dt_save_row` DateTime64(3) DEFAULT now64()
)
ENGINE = ReplicatedCollapsingMergeTree('/clickhouse/tables/tab1', '{replica}', sign)
PARTITION BY intDiv(id, 50000000)
ORDER BY (id_se, id, vid, stat, type)
SETTINGS index_granularity = 8192;
id_se почти все уникальные, id неуникальные, остальные поля неучавтсвуют в collapsing.
id_se стоит на 1ом месте в ORDER BY, так как вставка отмены строки происходит через поиск того, что там уже лежит, если поставить в конец ORDER BY, то скорость вставки медленная.
Все селекты с условием по id отдает быстро, но как только QPS поднимаем до 30-40, то CPU утилизируется под 100% и скорость selectов падает кратно(К примеру 10-15 qps отдает за 25-30ms, когда 30-40qps то запрос выполняется в диапазоне 500-800ms). 36 физических ядер, диск ssd, кол-во строк 800млн.
Какие есть варианты добиться высокого и быстрого qps, при быстрой вставке ?
Не знаю, подойдёт или нет. Но ведь в 22.5 или более ранней версии завозили функционал лимита нагрузки от пользователя.
версия на этой железке 21.2.9.41
запустите clickhouse-benchmark на чтение, поставьте 30 параллельных запросов, потом поиграйте с настройками SET max_threads=1, merge_tree_coarse_index_granularity=2 (or 1) > PARTITION BY intDiv(id, 50000000) Возможно партиционирование по id_se будет лучше, тк на анализ индекса будет тратиться меньше времени. index_granularity тоже можно опустить до 1024-2048
партиции сделал по id, чтобы выборки по id давали быстрый ответ. Вставки кстати в этот сервер не идут, на этой реплике только select. Если id_se поставлю в партицию, а id на первое место, кмк скорость селектов по id станет медленнее(
А есть корреляция между id и id_se
еще тут https://altinity.com/blog/clickhouse-in-the-storm-part-2 про use_uncompressed_cache написано, у нас памяти вагон(400ГБ есть свободных). Выставить в 1 и size поднять с 8ГБ может помочь?
Не уверен, если у вас там реально рандомный доступ
партиционирование по id только хуже сделало( max_threads=1 помогает, утилизация 40-45% при qps=40. Запросы конечно выполняются 250-400ms в среднем. merge_tree_coarse_index_granularity как будто почти не влияет( index_granularity=1024 тоже практически не помогает
merge_tree_coarse_index_granularity как будто почти не влияет( Мне помогало процентов на 20-30, когда анализ индекса занимает значительное время по сравнению с самим запросом. А покажи сам SELECT запрос и сколько партиций у вас?
партиций 8 Вот запрос, находу агрегирую collapsing select id, vid, stat, type, customer_id, param, toDecimal64(sum(sumin),2) as sumin, toDecimal64(sum(sumpv),2) as sumpv, uniqExact(id_se) as count, toDateTime(max(dt_save_row)) as maxver from ( SELECT gameid, vid, stat, type, customer_id, param, id_se, toDecimal64(sum(value1*sign),2) as sumin, toDecimal64(sum(value2*sign),2) as sumpv, dt_save_row FROM default.tab1 t WHERE id in (379248981) group by id, vid, stat, type, customer_id, param, id_se, dt_save_row having sum(sign)!=0) group by id, vid, stat, type, customer_id, param;
если так то да, помогает примерно на 10% не больше. То есть утилизация вместо 50% стала 45%
Хм, я скорее про тот который SELECT делает по id_se
Кстати не думали сделать реверс индекс просто?
так это же на вставку только для отмены строк состояния: insert into default.tab1 select *, negate(sign) from default.tab1 where id_se in (перечень id_se передаваемый сервисом)
подробнее чуть можно, что имеете ввиду?
Сделать таблицу с маппингом (id_se -> id) (ну над ток определится в каком порядке) Тогда можно через подзапрос доставать нужный id (или id_se) WHERE id IN (379248981) заменится на WHERE id IN (379248981) AND id_se IN (SELECT id_se FROM reverse_index WHERE id = 379248981))
да, я просто упростил, уже есть такая таблица именно так запрос и построен) У нас при вставке сюда сразу льется в отдельную таблицу, которая у нас как индексная, только мы ее используем и для других запросов. там id_se+id лежит.
но вот хороший вариант по двум условия фильтроваль, мы только по 2му из вашего примера
А у вас что более важно то? Сейчас у вас таблица оптимизирована для вставки больше, чем на чтение
8 партиций на 800 млн строк то есть, так как id_se high-cardinality колонка, любой запрос с id=... сканит 100 млн строк не было идеи сделать партиции поменьше?
Ну тогда вот вам еще на попробовать WHERE indexHint(id IN (379248981)) AND id_se IN (SELECT id_se FROM reverse_index WHERE id = 379248981)) WHERE id IN (379248981) AND indexHint(id_se IN (SELECT id_se FROM reverse_index WHERE id = 379248981)))
так и есть, 150-160млн строк сканит при запросах
Так вы делаете условие же на id_se как я понял, не должно 100 лям сканировать
как я понял, для пре-вставки условие по id_se для последующего чтения — по id
Да, но судя по этому > Сделать таблицу с маппингом (id_se -> id) (ну над ток определится в каком порядке) да, я просто упростил, уже есть такая таблица именно так запрос и построен)
запрос на чтение выглядит так и судя по clickhouse-benchmark у него rps 100-150млн К примеру вот такой запрос: select id, vid, stat, type, customer_id, param, toDecimal64(sum(sumin),2) as sumin, toDecimal64(sum(sumpv),2) as sumpv, uniqExact(id_se) as count, toDateTime(max(dt_save_row)) as maxver from ( SELECT gameid, vid, stat, type, customer_id, param, id_se, toDecimal64(sum(value1*sign),2) as sumin, toDecimal64(sum(value2*sign),2) as sumpv, dt_save_row FROM default.tab1 t WHERE id_se(select id_se from index_table where id in (379248981)) group by id, vid, stat, type, customer_id, param, id_se, dt_save_row having sum(sign)!=0) group by id, vid, stat, type, customer_id, param;
> него rps 100-150млн != что каждый запрос читает 100 лям строк)
У вас тут кх все партиции читает, так что добавить indexHint(id in (379248981)) поможет читать меньше данных
сорри, зашел в query_log и read_rows 1млн
Обсуждают сегодня