Как вы моделируете пользовательские данные в Cassandra?
У нас есть различные данные и метаданные, связанные с клиентом, которые в настоящее время хранятся в отдельных таблицах с одинаковыми ключами разделения и кластеризации.
Это приводит к извлечению для пользователя битов информации из разных таблиц (например, в аналитику), эффективно «объединяя» две или более таблиц Cassandra на их ключах разделов.
С положительной стороны, вставка в таблицы выполняется независимо.
Существует ли состояние гонки при одновременном обновлении данных под одним и тем же ключом раздела, но в разных столбцах? Или дельты изящно сливаются на SSTables?
Наличие нескольких таблиц с одинаковыми ключами разделов (и кластеризации) - обычное дело или анти-шаблон?
Чтобы сделать это более конкретным, предположим, что у нас есть:
CREATE TABLE example (
pk text PRIMARY KEY
col_a text
col_b text
)
Предположим, что для данного ключа раздела (pk) изначально и col_a, и col_b имеют некоторое значение (т.е. не ноль). и две одновременные вставки обновляют каждую из них. Возможны ли там какие-либо условия гонки? Потеряете одно из двух обновлений, несмотря на запись в отдельные столбцы?

Конфликты записи - это то, о чем вам не нужно беспокоиться. Все ВСТАВКИ / ОБНОВЛЕНИЯ / УДАЛЕНИЯ - это обновления в Кассандре. Все в Cassandra основано на столбцах.
Кассандра использует стратегию «последняя запись побеждает» для управления конфликтами. Как вы можете видеть на примере ниже, всякий раз, когда вы меняете значение, обновляется временная метка, связанная с этим столбцом. Поскольку вы выполняете одновременные обновления, один поток будет обновлять col_a, а другой - col_b.
Начальная вставка
cqlsh:test_keyspace> insert into race_condition_test (pk, col_a, col_b ) VALUES ( '1', 'deckard', 'Blade Runner');
cqlsh:test_keyspace> select * from race_condition_test ;
pk | col_a | col_b
----+---------+--------------
1 | deckard | Blade Runner
(1 rows)
Метки времени такие же в исходной вставке
cqlsh:test_keyspace> select pk, col_a, writetime(col_a), col_b, writetime(col_b) from race_condition_test ;
pk | col_a | writetime(col_a) | col_b | writetime(col_b)
----+---------+------------------+--------------+------------------
1 | Deckard | 1526916970412357 | Blade Runner | 1526916970412357
(1 rows)
После обновления col_b его временная метка изменяется, чтобы отразить это изменение.
cqlsh:test_keyspace> insert into race_condition_test (pk, col_b ) VALUES ( '1', 'Rick');
cqlsh:test_keyspace> select pk, col_a, writetime(col_a), col_b, writetime(col_b) from race_condition_test ;
pk | col_a | writetime(col_a) | col_b | writetime(col_b)
----+---------+------------------+-------+------------------
1 | Deckard | 1526916970412357 | Rick | 1526917272641682
(1 rows)
После обновления col_a его метка времени также обновляется до нового значения.
cqlsh:test_keyspace> insert into race_condition_test (pk, col_a) VALUES ( '1', 'bounty hunter');
cqlsh:test_keyspace> select pk, col_a, writetime(col_a), col_b, writetime(col_b) from race_condition_test ;
pk | col_a | writetime(col_a) | col_b | writetime(col_b)
----+---------------+------------------+-------+------------------
1 | bounty hunter | 1526917323082217 | Rick | 1526917272641682
(1 rows)
Я рекомендую вам использовать одну единственную таблицу, которая удовлетворяет вашим запросам. Если вам нужно выполнить запрос по pk, создайте одну единую таблицу со всеми необходимыми столбцами. Таким образом, у вас будет одна широкая строка, которую можно будет эффективно прочитать как часть одного запроса.
Модель данных, которую вы описываете в варианте 2, немного реляционная и не оптимальна для Cassandra. Вы не можете выполнять соединения изначально в cassandra, и вам следует избегать предварительного формирования соединений на стороне клиента.
Правила режима данных:
Правило 1: Равномерно распределять данные по кластеру Вам нужно будет создать ключ раздела, который обеспечит равномерное распределение данных по кластеру и отсутствие горячих точек.
Правило 2: Минимизировать количество разделов на чтение Каждый раздел может находиться на разных узлах, поэтому вы должны попытаться создать сценарий, в котором ваши запросы в идеале направляются только на один узел для повышения производительности.
Правило 3: Модель вокруг ваших запросов
Если вы хотите прочитать об этом немного больше, я предлагаю эту страницу с данными о данных: Основные правила моделирования данных Cassandra