Допустим, у меня есть данные о заказах клиентов, поступающие в мою службу, и я хотел бы сделать некоторые отчеты по этим данным. Все заказы клиентов сохраняются в таблице Cassandra, поэтому я могу получить все заказы для данного клиента:
TABLE customer_orders
store_id uuid,
customer_id text,
order_id text,
order_amount int,
order_date timestamp,
PRIMARY: KEY (store_id, customer_id)
Но я также хотел бы найти всех клиентов с заданным количеством заказов. В идеале я хотел бы иметь это в готовой к запросу таблице в Cassandra. Например, «получить всех клиентов, у которых есть 1 заказ».
Поэтому у меня есть такая таблица:
TABLE order_count_to_customer
store_id uuid,
order_count int,
customer_id text
PRIMARY KEY ((store_id, order_count), customer_id)
Таким образом, идея заключается в том, что когда поступает заказ, обе эти таблицы должны быть обновлены.
Поэтому я создаю третью таблицу:
TABLE customer_to_orders_count
store_id uuid,
customer_id text,
orders_count counter,
PRIMARY KEY (store_id, orders_count)
Когда приходит заказ:
Я сохраняю его в первой таблице
Затем обновите счетчик в третьей таблице, увеличив его на 1.
Затем я читаю счетчик в третьей таблице и вставляю новую запись во вторую таблицу.
Когда мне нужно найти всех клиентов с заданным количеством заказов, я просто запрашиваю вторую таблицу.
Проблема в том, что счетчики не являются атомарными и непротиворечивыми. Если я обновлю счетчик, скажем, до 3, нет никакой гарантии, что когда я прочитаю его в следующий раз, чтобы обновить вторую таблицу, это будет 3. Это может быть 2. Даже если я прочитаю счетчик до того, как я обновлю счетчик, он может быть некоторым значением на несколько шагов назад. Так что тоже никакой гарантии. Обратите внимание, что я знаю об ограничениях счетчиков в Cassandra и не спрашиваю, как решить проблему со счетчиками.
Я скорее привожу этот пример, чтобы попросить несколько общих советов о том, как смоделировать данные, чтобы иметь возможность выполнять их совокупный подсчет. Конечно, я могу использовать Spark для выполнения агрегированных запросов непосредственно к первой таблице в моем примере. Но мне кажется, что можно было бы сделать это более умным способом, а также Spark потребовал бы переноса всех данных таблицы в память.