2016-12-04 1 views
2

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

Пользователь может написать сообщение. Другие пользователи могут ответить на сообщение. Пользователи также могут «проголосовать» или «проголосовать» за сообщение. Пользователь сортирует сообщения по дате или по голосам или голосам вниз.

Это мое определение таблицы -

CREATE TABLE post.comments_by_post (
postid text, 
parentpostid text, 
createdon bigint, 
username text, 
userid text, 
displayname text, 
upvotes int, 
downvotes int, 
comment text, 
PRIMARY KEY ((postid, parentpostid), createdon) 
) WITH CLUSTERING ORDER BY (createdon DESC); 

Для увеличения "upvote" У меня есть обновление запроса -

UPDATE post.comments_by_post SET upvotes = incrementedValue where postid=1 and parentpostid = 2 ; 

incrementedValue рассчитывается прибавлением 1 в предыдущем значении.

incrementedValue = PreviousValue + 1

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

Есть ли у нас лучший способ?

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

ответ

1

В приведенной ниже таблице и вторичный индекс позволит осуществить подсчет без таблицы счетчика и без каких-либо блокировок:

CREATE TABLE votes_by_comment (
    postid text, 
    parentpostid text, 
    userid text, 
    vote text, //can be 'up' or 'down' 
PRIMARY KEY ((postid, parentpostid), userid)) 

CREATE INDEX ON votes_by_comment (vote); 

Когда пользователь делает «вверх голосов»:

INSERT INTO votes_by_comment (postid, parentpostid, userid, vote) VALUES ('comment1', 'post1', 'user1', 'up'); 

Когда пользователь делает 'вниз' голосов:

INSERT INTO votes_by_comment (postid, parentpostid, userid, vote) VALUES ('comment1', 'post1', 'user1', 'down'); 

userid как кластеризация со lumn позволит ему избежать состояния гонки и ограничить множественное голосование одним пользователем.

Подсчитать голоса:

SELECT count(*) from votes_by_comment WHERE postid='comment1' AND parentpostid='post1' and vote='up'; 

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

Но этот подход не позволяет выполнять заказ по голосам на стороне Кассандры, и он должен быть реализован на стороне приложения.

+0

Вставка записи решит все проблемы с условиями гонки. – Gunwant

1

Вы потеряете некоторое обновление при возникновении параллельного обновления.
Ex. Пользователь A прочитал текущее значение, скажем 10. В то же время Другой пользователь B также прочитает текущее значение, которое получит 10. Затем пользователь A сделает запрос на обновление с новым значением 11. И тогда пользователь B также сделает запрос на обновление новым значением 11. Так вы потеряли обновление User A.

Стол-стол - лучший выбор для вас.

Счетчик - специальный столбец, используемый для хранения числа, которое изменяется с шагом. Счетчики Кассандры были переработаны в Кассандре 2.1, чтобы облегчить некоторые трудности. Прочтите What’s New in Cassandra 2.1: Better Implementation of Counters, чтобы узнать о улучшениях, внесенных в счетчики.

Вы можете создать счетчик таблицы, как это:

CREATE TABLE vote_counter (
    postid text, 
    parentpostid text, 
    upvotes counter, 
    downvotes counter, 
    PRIMARY KEY((postid,parentpostid)) 
) 

Теперь вы можете запросить как этот:

UPDATE vote_counter SET upvotes = upvotes + 1 WHERE postid = ? AND parentpostid = ? 
UPDATE vote_counter SET upvotes = upvotes - 1 WHERE postid = ? AND parentpostid = ? 
UPDATE vote_counter SET downvotes = downvotes + 1 WHERE postid = ? AND parentpostid = ? 
UPDATE vote_counter SET downvotes = downvotes - 1 WHERE postid = ? AND parentpostid = ? 
+0

Спасибо, Ashraful. Создание другой таблицы приведет к нарушению правила для моделирования данных. Как один запрос для чтения данных. – Gunwant

1

Из Вашего описания:

.. .User сортировать сообщения по дате или по голосам или голосам вниз.

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

CREATE TABLE post.comments_by_post (
    postid text, 
    parentpostid text, 
    createdon bigint, 
    username text, 
    userid text, 
    displayname text, 
    upvotes int, 
    downvotes int, 
    comment text, 
    PRIMARY KEY ((postid, parentpostid), upvotes) 
) WITH CLUSTERING ORDER BY (createdon DESC); 

Если вы обновить C * и перейти с 3.0, вы можете сэкономить немало работы и создать Materialized View.

Назад к проблеме параллелизма, подсчет в распределенной среде действительно сложно. В зависимости от ваших требований, то я предлагаю вам два возможное решение:

1) Вам не нужно быть точным (вы можете терпеть над/под подсчетами). В этом случае я предлагаю вам пойти с новой таблицей счетчиков Cassandra для хранения ваших счетчиков. Основным недостатком этого подхода является то, что вы фактически теряете способность получать (с вашей точки зрения приложения) свои результаты в порядке, поэтому вам необходимо применить заказ на рычаге приложения. Вы также сохраняете две другие таблицы, описанные выше, потому что счетчики остаются в другой таблице.

2) Вы должны быть точным. В этом случае вам потребуется сериализовать доступ к каждому счетчику сообщений. Это можно сделать, сохранив небольшой кеш почтовых счетчиков, которые вы собираетесь обновить или недавно обновили, и приобретите блокировку на каждый элемент уровня приложения каждый раз, когда вы хотите его обновить. Должно быть достаточно 64k сообщений. Теперь вы знаете, что для каждого сообщения вы выполняете обновления последовательно. Вы не можете ошибиться в этом, потому что вы не применяете глобальный замок, вы применяете только местных замков. Вам все еще нужны три таблицы с C * 2.0 или один + материализованный вид с C * 3.0.

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