184 похожих чатов

Добрый день, здравствуйте! Можете, пожалуйста помочь. Таблица работает на движке ReplacingMergeTree,

логика такая: у нас есть компонент в системе, который выполняет какую-то работу, попутно сохраняя "снимки" данных в процессе. Условно, начал работу — сделал снимок, произошло какое-то событие в системе — сделал снимок и так далее. Идея ясна. Все они имеют почти идентичные значение полей (например, отличается last_event_time).

У клиента есть желание видеть самые актуальные значения в реальном времени. В базе у нас содержатся "дубликаты", из которых нужно выбрать один самый актуальный. При каждом запросе чистить дубликаты (а Clickhouse это позволяет) слишком дорого. Для меня решением стал запрос:

SELECT
argMax(hex(id), last_event_time)
argMax(field_1, last_event_time),
argMax(field_2, last_event_time),
FROM (
SELECT id, field_1, field_2, field_3, last_event_time -- ...
FROM table_1
WHERE (service_timestamp > toDateTime64('1694165200', 6, 'UTC') AND service_timestamp < toDateTime64('1696957199', 6, 'UTC'))
UNION ALL
SELECT id, field_1, field_2, field_3, last_event_time -- ...
FROM table_2
WHERE (service_timestamp > toDateTime64('1694165200', 6, 'UTC') AND service_timestamp < toDateTime64('1696957199', 6, 'UTC'))
)
GROUP BY (id)
ORDER BY (service_timestamp) ASC
LIMIT 0, 100;

argMax() вернет строку m с максимальным значением n.

m | n
1 | 2
1 | 3
2 | 4
4 | 4

SELECT argMax(m, n) .. GROUP BY (m)
===============================
1 | 3
2 | 4
4 | 4

Однако при большом наборе полей памяти используется невероятно много. Вопрос: как обычно решается такая проблема? Именно Best Practice.

Я не администратор, опыта работы с базами данных у меня мало. Буду признателен за помощь!

10 ответов

16 просмотров

argMax вполне good practice. Еще вариант, выбрать максимальное значение timestamp и сджойнить с основной таблицей SELECT * FROM (SELECT * FROM table) as full RIGHT SEMI JOIN (SELECT max(service_timestamp) as service_timestamp, id ) as maxTime USING service_timestamp, id

Алексей Осипов
Не тяжеловатый джойн будет ?

С ограничениями по Where нормально. В чистом виде конечно не пойдет

из БД в ПЗУ такое грести накладно.. Проще на коленке написать отдельную приложуху на любом ЯП или использовать Redis и постоянно обновлять по ключу данные.. Тоже пример "на котиках", но логику вы поймёте: есть N светофоров - id 1, 2, 3,... и в каждый момент времени у вас поступает связка: (id, сигнал(зел/жел/кр), timestamp) -> в редиску перезаписывать для каждого id: 1, кр, 1696912696 3, зел, 1696912697 ... Если у вас ваши поля НЕ огромные, вы там не храните видео и прочее и оборудования - не миллиарды миллиардов, тогда вы в каждый момент времени имеете САМЫЕ просто актуальные данные и достаточно получить их из редиски той же по ключу - это считанные миллисекунды, даже учитывая, если у вас редиска будет далеко.. А как вам советуют с джойнами и прочим - это накладно ОЧЕНЬ - "из пушки по воробьям". Посмотрите в сторону in-memory хранения последнего стейта, чтоб не выгребать.. БД всё же нужна для ретроспективы у вас, а не для последнего стейта..

Владислав-Белоусов Автор вопроса
DaiTenshi
из БД в ПЗУ такое грести накладно.. Проще на колен...

Приложение, которое генерирует снимки данных, как раз работает с Redis. И занимается планированием — там он к месту. А Clickhouse работает в качестве исторической справки. Тут он используется по назначению: клиенты могут захотеть выгрузить и посмотреть на историю за прошлую неделю, месяц или даже год. Тут Redis не поможет, к сожалению)

Владислав Белоусов
Приложение, которое генерирует снимки данных, как ...

Для ретро - запрос в клик, последнее в редиске в качестве хранилища ключ-значение. Видимо, там редиска используется как мессадж бас. А раз редиска уже есть, почему бы не выделить там ещë kvp хранилище? Я не говорил, что вам не нужна ретроспектива, но для последних значений лучшей практикой будет подрубить in-memory kvp хранилище.

Владислав-Белоусов Автор вопроса
DaiTenshi
Для ретро - запрос в клик, последнее в редиске в к...

Слишком большой объём данных. У нас огромный трафик, и Clickhouse себя зарекомендовал великолепно) С этим помощь не нужна, спасибо). Мне нужна только помощь с оптимизацией запроса)

Сделать mv с AggregatingMT движком и argMaxState не вариант? У нас похожий кейс, нужно выдавать статусы на максимальную дату, по сути текущие статусы. Уникальных записей под полмиллиона, размер - единицы гигабайт за год на диске всего

Владислав-Белоусов Автор вопроса
Ivan Torgashov
Сделать mv с AggregatingMT движком и argMaxState н...

Я в эту сторону не смотрел, спасибо, обязательно посмотрю. Тут как раз выше кидали статью с MV. А насколько ощутима разница между "оптимизированным запросом" и MV было в вашем случае, если не секрет? Я вот добился неплохой производительности просто добавив LIMIT внутрь подзапроса: SELECT argMax(hex(id), last_event_time) argMax(field_1, last_event_time), argMax(field_2, last_event_time), FROM ( SELECT id, field_1, field_2, field_3, last_event_time -- ... FROM table_1 WHERE (service_timestamp > toDateTime64('1694165200', 6, 'UTC') AND service_timestamp < toDateTime64('1696957199', 6, 'UTC')) LIMIT 0, 100; -- добавил тут UNION ALL SELECT id, field_1, field_2, field_3, last_event_time -- ... FROM table_2 WHERE (service_timestamp > toDateTime64('1694165200', 6, 'UTC') AND service_timestamp < toDateTime64('1696957199', 6, 'UTC')) LIMIT 0, 100; -- добавил тут ) GROUP BY (id, ...) ORDER BY (...) ASC LIMIT 0, 100;

Владислав Белоусов
Я в эту сторону не смотрел, спасибо, обязательно п...

в исходной таблице несколько миллиардов записей, допустим для миллиона уникальных клиентов. При аггрегации все это схлапывается до миллиона получается, по ним и идёт поиск по дата+стейт, либо просто стейт (в нашем случае с датой), даже за год всего 365млн строк, боюсь даже посчитать сколько в сырой будет за это время (храним макс. несколько месяцев оригинал). Так вот по MV поиск по конретному клиенту менее одной секунды, по сырью ддо нескольких минут, в зависимости от нагрузки

Похожие вопросы

Обсуждают сегодня

https://github.com/erlang/otp/blob/OTP-27.1/lib/kernel/src/logger_h_common.erl#L174 https://github.com/erlang/otp/blob/OTP-27.1/lib/kernel/src/logger_olp.erl#L76 15 лет назад...
Maksim Lapshin
20
Ребят в СИ можно реализовать ООП?
Николай
33
~ 2m21s  nix shell github:nixos/nixpkgs#stack ~  stack ghc -- --version error: … while calling the 'derivationStrict' builtin at /builtin/derivation.nix:...
Rebuild your mind.
6
Добрый вечер, Пока не совсем понимаю как наладить общение между телеграм ботом и ПО для работы с сим боксом. По самому боту так понял: - Нужен некий баланс, который можно поп...
Magic
6
core\config\database\connections\default.php На всякий случай проверь всё же файл <?php return [ 'driver' => env('DB_TYPE', 'mysql'), //$database_type 'host' => env('D...
Andrey K
2
Всем доброго вечера. Разрабатываю 32 раз. приложение в Delphi. Столкнулся с тем, что стандартный  TFilestream  не работает с большим файлом > 2 ГБайт (после вызова функции see...
Vadim Gl
16
Всем привет! Имеется функция: function IsValidChar(ch: UTF8Char): Boolean; var i: Integer; ValidChars: AnsiString; begin ValidChars := 'abcdefghijklmnopqrstuvwxyzABCDE...
Евгений
44
добрый день. Подскажите, есть сайт на 1.4.7 и я хочу обновиться, особо ничего не меняя. мне выбирать версию 1.4.35 или третью ветку? и можно ли обновлять "как есть", или нужно...
Digital Cat
12
народ, плиз хелп, всю голову сломал себе уже... разве может быть так, что GetProcAddress( GetModuleHandle( "kernel32.dll" ), "SetThreadDescription" ) вернёт ненулевое значение...
Iluha Companets
12
Кто кодит под Лазарем на винде, у вас аналогично VCL переопределяются CreateWnd и CreateParams для конкретных классов контролов и все заданные флаги влияют?
А Андрей
11
Карта сайта