Разбиение на страницы запросов с использованием minTimeuuid и maxTimeuuid

У меня есть таблица cassandra с первичным ключом Timeuuid, и я хотел бы обрабатывать всю таблицу по частям на различных узлах обработки.

Итак, я подумал, что могу разбить обработку, используя minTimeuuid/maxTimeuuid:

  • Чтобы начать свою работу, каждый узел должен получить временной диапазон, который они должны получить от Cassandra, запросить его и обработать.
  • Плотность данных довольно однородна, поэтому проблем быть не должно.

Что меня смущает, если я сделаю это:

   SELECT * FROM myTable
   WHERE t > maxTimeuuid('2013-01-01 00:05+0000')
   AND t < minTimeuuid('2013-02-02 10:00+0000')

В документации говорится:

В примере min/maxTimeuuid выбираются все строки, в которых столбец timeuuid t строго позже 01:05+0000 2013-01-00, но строго раньше 02-02-2013 10:00+0000. t >= maxTimeuuid('2013-01-01 00:05+0000') не выбирает timeuuid, сгенерированный точно в 2013-01-01 00:05+0000, и по существу эквивалентен t > maxTimeuuid('2013-01 -01 00:05+0000').

Итак, насколько я понимаю, если бы я выбрал диапазон, начинающийся с «2013-02-02 10:00+0000» для следующего фрагмента, я бы пропустил данные, относящиеся именно к этому времени, потому что ни один из них не охватывает эту точную дату.

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



ИЗМЕНИТЬ:

Итак, очевидно, я не могу выполнять запросы диапазона на Timeuuid. Как и просили, вот моя таблица:

CREATE TABLE cgr.reports (
    pk_1 text,
    pk_2 text,
    pk_3 bigint,
    pk_4 bigint,
    some_data text,
    PRIMARY KEY ((pk_1, pk_2, pk_3, pk_4))
);

PK на самом деле не обязательно должен быть совокупностью этих 4, потому что в какой-то момент я понял, что для них не планируется никаких запросов. Именно тогда я подумал, что могу сделать запрос диапазона для Timeuuid, поэтому я подумал, что могу сделать это вместо этого:

CREATE TABLE cgr.reports (
    pk_uuid Timeuuid,
    data_1 text,
    data_2 text,
    data_3 bigint,
    data_4 bigint,
    some_data text,
    PRIMARY KEY ((pk_uuid))
);

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

Спасибо за вашу помощь!


person Code Herder    schedule 12.08.2015    source источник
comment
Можете ли вы опубликовать свое определение таблицы? Кроме того, вы не можете запросить диапазон для ключа секции, поэтому вам понадобится ключ секции, а затем ключ кластеризации для запроса вашего диапазона.   -  person Aaron    schedule 12.08.2015


Ответы (1)


Вы можете сделать это следующим образом:

CREATE TABLE cgr.reports (
    timebucket int,
    pk_uuid Timeuuid,
    data_1 text,
    data_2 text,
    data_3 bigint,
    data_4 bigint,
    some_data text,
    PRIMARY KEY (timebucket, pk_uuid)
);

Где timebucket — это pk_uuid по модулю some_number. some_number должно быть достаточно большим, чтобы разделить данные поровну между вашими узлами, и достаточно низким, чтобы агрегировать некоторый объем данных для ваших рабочих процессов, чтобы не выполнять частые запросы по множеству небольших фрагментов. Каждому работнику будет назначено напоминание о разделении, и он будет обрабатывать только эти значения.

Однако идеальный способ будет таким:

CREATE TABLE cgr.reports (
    pk_uuid Timeuuid,
    data_1 text,
    data_2 text,
    data_3 bigint,
    data_4 bigint,
    some_data text,
    PRIMARY KEY (data_1, pk_uuid)
);

Где data_1 имеет большую кардинальность и известна вашим воркерам. Это разделяет данные поровну на ваши кластеры, а запросы диапазона времени разрешены для pk_uuid. Каждый рабочий процесс присвоил значения data_1 и обрабатывает только эти значения.

Изменить: Timeuuid выбирает объяснение:

Я не проверял это, однако мое понимание таково:

Timeuuid — это, по сути, Time+UUID. Поэтому, если вы можете задавать Кассандре только запросы:

t > minTimeuuid(x) AND t < maxTimeuuid(y)

где x ‹ y, вы выберете время в диапазоне (x_000, y_999) — _abc — это clockid + nodeid.

Но что после y_999? Это (y+1 тик)_000 - это minTimeuuid(y+1). Поэтому выполнение запроса:

t > minTimeuuid(x) AND t < minTimeuuid(y+1)

вы выберете время в диапазоне (x_000, y+1_000). Вы не выберете y+1_000 или y+1_389, но выберете y_999.

Следующий запрос рядом с этим будет:

t > maxTimeuuid(y) AND t < minTimeuuid(z+1)

временной диапазон здесь (y_999, z+1_000). Поэтому вы не выберете y_999.

Однако имейте в виду, что это только мое понимание, и, пожалуйста, дайте мне знать после того, как вы протестируете его, если это работает. Более того, в зависимости от вашего драйвера это может отличаться, как показано в реализации драйвера C#: http://nickberardi.com/sometimes-a-nanosecond-makes-all-the-difference/

person piotrwest    schedule 13.08.2015
comment
Я понимаю, о чем вы говорите, это хорошая идея, чтобы разделить работу таким образом. Единственная часть, которую я не понимаю, - это то, как работают запросы диапазона, поскольку границы не включаются. Насколько я понимаю, если верхний диапазон окна равен 2013-02-02 10:00+0000, а следующая нижняя граница такая же (2013-02-02 10:00+0000), будет небольшая трещина, где строки может проскочить в теории. - person Code Herder; 13.08.2015
comment
@CodeHerder Я обновил свой ответ. Однако это теория, поэтому, пожалуйста, проверьте ее и дайте мне знать, работает ли она. - person piotrwest; 14.08.2015