2015-02-09 4 views
2

Итак, я разрабатываю эту модель данных для отслеживания цены продукта.Моделирование данных Кассандры

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

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

Так первоначально я думал о следующей модели продукта:

Однако «subscriberEmails» представляет собой сборник список, который будет обрабатывать до 65536 элементов. Но, будучи большим решением для данных, это граница, которую мы не хотим иметь. Таким образом, мы в конечном итоге писать отдельную таблицу для этого:

Так что теперь «usersByProduct» может иметь до 2 млрд столбцов, достаточно справедливо. И пользовательские предпочтения сохраняются в «Карте», которая снова ограничена, но мы считаем, что это хорошее максимальное количество продуктов для пользователя.

Теперь проблема мы сталкиваемся заключается в следующем:

Каждый раз, когда мы хотим обновить цену товара мы должны сделать запрос следующим образом:

INSERT INTO products("Id", date, price) VALUES (7dacedd2-c09b-46c5-8686-00c2a03c71dd, dateof(now()), 24.87); // Example only 

Но ВСТАВИТЬ операции DON» t допускают другие условные предложения, чем (ЕСЛИ НЕ СУЩЕСТВУЕТ), и это не то, что мы хотим. Нам нужно обновить цену, только если она отличается от предыдущей, поэтому это вынуждает нас делать два запроса (один для чтения текущего значения, а другой - для его обновления, если это необходимо).

PD. Операции UPDATE имеют условия IF, но это не наш случай, потому что нам нужен INSERT.

UPDATE products SET date = dateof(now()) WHERE "Id" = 7dacedd2-c09b-46c5-8686-00c2a03c71dd IF price != 20.3; // example only 
+0

Просто любопытно, но зачем вам нужен «INSERT»? Под капотом «UPDATE» и «INSERT» одинаковы: http://stackoverflow.com/questions/28350630/when-are-rows-overwritten-in-cassandra/28351184#28351184 – Aaron

+0

Потому что, хотя я сделал все операции UPDATES, я все равно должен предоставить временную метку (которая находится в первичном ключе) для вставки определенной цены продукта. При вводе новой цены в заданное время новое время не существует в базе данных, и по этой причине операция обновления всегда будет рассматриваться как вставка (и не будет проверять, совпадает ли последняя вставленная цена). – user1799563

+0

Кроме того, вы не хотите этого делать: 'dateof (now())'. 'DateOf' возвращает метку времени, но' now' возвращает timeuuid, которые имеют разную точность. Это означает, что ваши данные будут иметь временную метку, но на самом деле будут хранить timeuuid, сгенерированный из 'now()', что затрудняет вам запрос по диапазону дат (если это то, что вы планируете делать): http : //stackoverflow.com/questions/26237940/cassandra-cql-select-query-not-returning-records-which-have-timestamp-as-cluster/26239367#26239367 – Aaron

ответ

2

Не пытайтесь применить обычную модель к базе данных cassandra. Он может работать, но вы получите ужасную производительность и масштабируемость.

Рекомендуемый подход к моделированию данных Cassandra заключается в том, чтобы сначала выяснить ваши запросы на чтение к базе данных и структурировать ваши данные, чтобы эти чтения были дешевыми. Вероятно, вам придется дублировать несколько писем, но это нормально, потому что в Кассандре довольно дешевые записи.

Для вашего конкретного случая использования ключа запроса, кажется, чтобы иметь возможность получить все пользователи, заинтересованные в изменении цены на продукт, так что вы создаете таблицу для этого, например:

create table productSubscriptions ( productId uuid, priceLimit float, createdAt timestamp, email text, primary key (productId,priceLimit,createdAt) );

но так как вы должны знать все подписки продукта для пользователя, вы все должны пользователем ключом таблицу те же данные:

create table userProductSubscriptions ( email text, productId uuid, priceLimit float, primary key (email, productId) )

с этими 2 таблицами, я думаю, вы можете увидеть, что все ваши основные запросы могут быть выполнены с помощью выбора одной строки, а ваша вставка/удаление проста, но вам потребуется изменить обе таблицы в синхронизации.

Очевидно, что вам понадобится немного больше схемы для вашей полной необходимости, но это должно дать вам пример о том, как думать о вашей схеме cassandra.

Условный выпуск обновления

Для вашего условного выпуска вставки, самый простой ответ: это делать с UPDATE, если это действительно необходимо (обновление и вставки почти идентичны в CQL), но это очень дорогостоящая операция поэтому избегайте этого, если сможете.

Для вашего случая использования, я бы разделить таблицу продукта в трех:

create table products ( category uuid, productId uuid, url text, price float, primary key (category, productId) ) create table productPricingAudit ( productId uuid, date timestamp, price float, primary key (productId, date) ) create table priceScheduler ( day text, checktime timestamp, productId uuid, url text, primary key (day, checktime) )

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

продуктPricingAudit woul d имеет вставку с последней ценой извлечь то, что это, так как это позволит вам отлаживать любой ценовой вопрос вы можете иметь

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

С такой схемой вы не заботитесь об условных обновлениях, вы просто выпускаете 3 вставки при каждом обновлении цены продукта, даже если она не изменяется.

+0

Спасибо за ваш ответ, это выглядит нормально и управляемо, и я почти уверен, что включу его в схему. Однако моя главная проблема заключается в том, что я не могу условно вставить новую цену на существующий продукт ... То есть проверяет, отличается ли последняя существующая цена от той, которая будет вставлена. – user1799563

+0

Я обновил свой ответ для решения этой проблемы – rluta

+0

«productPricingAudit будет иметь вставку с последней ценой, полученной независимо от того, что это будет, так как это позволит вам отладить любую проблему с ценой, которую вы можете иметь» Хорошо, спасибо снова, но так много вставок точно, чего я пытаюсь избежать. Я не хочу хранить каждую цену, только возможные изменения. Зачем? Потому что у меня будет еще 20 рабочих, которые постоянно проверяют цены, и я, вероятно, в конечном итоге получаю вставки 1M + каждый день, если буду следовать этой модели. Вот почему мне нужно сохранить новую цену только тогда, когда она изменится. – user1799563

1

Хорошо, я постараюсь ответить на свой вопрос: условные вставки, отличные от «ЕСЛИ НЕ СУЩЕСТВУЮТ», не поддерживаются в Кассандре к дате, периоду.

Ближайшая вещь - это условное обновление, но это не работает в нашем сценарии. Итак, есть один простой вариант: логика приложения. Это означает, что вы должны прочитать предыдущую запись и принять решение по вашей заявке. Очевидным недостатком является то, что выполняются 2 запроса (один SELECT и один INSERT), который, очевидно, добавляет латентность.

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

Итак ... Запрос похож на это будет выполняться каждые X минут:

SELECT id, url, price FROM products WHERE "nextCheckTime" < now();  

// example only, wouldn't even work if nextCheckTime is not part of the PK or index 

Это очень дорогостоящая операция для выполнения на кластере Кассандрой, потому что он должен пройти через все строки, которые хранятся случайным образом в разных узлах по умолчанию. Еще одним недостатком является то, что нам нужны некоторые передовые и конкретные статистические данные о продуктах и ​​пользователях.

Итак, мы решили, что реляционная база данных будет служить нам лучше, чем Кассандра в данном конкретном случае.

Мы, к сожалению, оставляем все преимущества Cassandra (быстрые вставки, простое масштабирование, встроенные в очертания ...) и смотрим на реализацию MySQL Cluster или master-slave.

Смежные вопросы