Использование CollapsingMergeTree

253 views
Skip to first unread message

Timur Karimbaev

unread,
Nov 25, 2016, 2:08:00 AM11/25/16
to ClickHouse
Господа, подскажите пожалуйста

если я верно понял документацию - для изменяемых данных, типа визитов, вы используете CollapsingMergeTree

Представим что при каждом хите у визита меняется кол-во хитов, время последнего визита и адрес последней страницы (к примеру это поле "Страница выхода")

Соответственно, нужно сделать одну запись в таблицу хитов и 1-2 записи (с разным Sign) в таблицу визитов

При этом, та запись что с минусом - она должна повторять "предыдущую" запись в таблице визитов с плюсом, так?
Т.е., чтобы не хранить состояние где-то еще - мне при каждой записи в таблицу визитов нужно достать оттуда последнюю запись (SELECT'ом), а потом сделать insert ее с минусом?

такое использование кажется странным (то что нужно делать select одиночной записи каждый раз), поэтому думаю что я неправильно понял идею. 
Еще можно где-то снаружи clickhouse хранить последнюю вставленную информацию, но это кажется еще более странным :) 
Еще есть вариант писать изменения в таблицу визитов не при каждом хите, а отдельно логгируя какие визиты менялись, но тогда нужно где-то держать список хитов которые вставились

В общем все варианты которые я придумал кажутся неправильными, поэтому уточняю - как делать "правильно"?

Timur Karimbaev

unread,
Nov 25, 2016, 2:29:01 AM11/25/16
to ClickHouse
Понял что вариант с селектом в момент записи хита не подходит, атомарности не будет (до этого думал в сторону AggregatingMergeTree, там так наверно можно было сделать)

пятница, 25 ноября 2016 г., 10:08:00 UTC+3 пользователь Timur Karimbaev написал:

Dmitriy Gladkih

unread,
Nov 25, 2016, 2:52:59 AM11/25/16
to ClickHouse
При использовании CollapsingMergeTree работаем со старым и новым состояниями (набором параметров): { old_state, new_state }.

1) Открывается страница, счетчик имеет только новое состояние: new_state. На сервер посылается запрос: { null, new_state }. В CH записывается строка:
(1, 'user_uuid_0', 'visit_id_0', 'url', '', time_on_page_10_sec)

2) Пользователь выполняет клик через 5 секунд и мы хотим эту информацию сохранить. old_state = copy(new_state). На сервер посылается запрос: { old_state, new_state }. В CH записывается 2 строки:

(-1, 'user_uuid_0', 'visit_id_0', 'url', '', time_on_page_10_sec)
(1, 'user_uuid_0', 'visit_id_0', 'url', 'last_click_url_0', time_on_page_15_sec)

В итоге, в CH будут записи:

(1, 'user_uuid_0', 'visit_id_0', 'url', '', time_on_page_10_sec)
(-1, 'user_uuid_0', 'visit_id_0', 'url', '', time_on_page_10_sec)

(1, 'user_uuid_0', 'visit_id_0', 'url', 'last_click_url_0', time_on_page_15_sec)

Для PK (user_uuid, visit_id) записи могут быть схлопнуты, после чего останется только такая запись:

(1, 'user_uuid_0', 'visit_id_0', 'url', 'last_click_url_0', time_on_page_15_sec)

3) Допустим, что еще через 10 секунд пользователь выполнил еще клик. old_state = copy(new_state). На сервер посылается запрос: { old_state, new_state }. В CH записывается 2 строки:

(-1, 'user_uuid_0', 'visit_id_0', 'url', 'last_click_url_0', time_on_page_15_sec)
(1, 'user_uuid_0', 'visit_id_0', 'url', 'last_click_url_1', time_on_page_25_sec)

В итоге, в CH будут записи:

(1, 'user_uuid_0', 'visit_id_0', 'url', '', time_on_page_10_sec)
(-1, 'user_uuid_0', 'visit_id_0', 'url', '', time_on_page_10_sec)

(1, 'user_uuid_0', 'visit_id_0', 'url', 'last_click_url_0', time_on_page_15_sec)
(-1, 'user_uuid_0', 'visit_id_0', 'url', 'last_click_url_0', time_on_page_15_sec)

(1, 'user_uuid_0', 'visit_id_0', 'url', 'last_click_url_1', time_on_page_25_sec)

Таким образом, 'обновляется' информация о последнем клике и времени на странице.

Если нам нужно, например, получить статистику по url, то будет такой запрос:

SELECT url, sum(sign) AS cnt FROM tbl GROUP BY url HAVING cnt > 0

Если нужно получить среднее время на странице:

SELECT sum(time_on_page * sign) / sum(sign) AS avg_time_on_page FROM tbl

Timur Karimbaev

unread,
Nov 25, 2016, 3:03:48 AM11/25/16
to ClickHouse
Dmitriy, я видимо не совсем точно выразился
Хиты (в терминологии метрики) - это посещения страницы. Визит - это группа хитов

По мере добавления хитов нужно менять визит. Проблема в том что в CollapsingMergeTree для того чтобы изменить состояние, нужно знать старое состояние (в отличие от реляционных моделей где update делается без этого).

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

Весь вопрос в том где хранить состояние. Если clickhouse умеет как-то это делать внутри себя (например атомарно выдать старое состояние и записать новое, либо как-то хитро схлопывать состояния как в AggregatingMergeTree) - это один кейс, и можно писать в визиты при каждом хите. Если нет - их нужно куда-то группировать и писать отдельно, без конкурентных запросов




пятница, 25 ноября 2016 г., 10:52:59 UTC+3 пользователь Dmitriy Gladkih написал:
Reply all
Reply to author
Forward
0 new messages