2016-02-15 4 views
0

Я столкнулся с проблемами потерянных записей, обновляя ряд в Кассандре. Вот моя схема:Утерянные обновления в Cassandra

create table balances(
id bigint, 
balance decimal, 
last_transaction_id bigint, 
update_timestamp timestamp, 
type varchar, 
is_balance_valid boolean,  
primary key (wallet_id) 
)  

Всего узлов в кластере: 3 в локальной фактор репликации DC: 2 Cassandra Версия: 2.1.8

Я обновить значение столбца «баланса» каждый раз, когда пользователь выполняет транзакцию, считывая ранее установленное значение, добавляя сумму транзакции и выдавая обновление. Я использую Java, драйвер Datastax (2.1.5).

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

Сделка # 1

10 фев 2016 18: 15: 16984 - [бассейн-11-токарно-1] - INFO - ScratchpadMasterStreamProcessor.processMessage (62) - Печать ул ID: 1466140282Scratchpad ID: 9127013322

10 февраля 2016 18: 15: 16986 - [бассейн-11-поточно-1] - ОТЛАДКА - SclwBalanceUpdater.updateBalance (43) - Текущий баланс: 0,0

10 февраля 2016 18: 15: 16986 - [бассейн-11-поточно-1] - ОТЛАДКА - SclwBalanceUpdater.updateBalance (44) - Отклонение: 200.0

10 февраля 2016 18: 15: 16986 - [бассейн -11-нить-1] - ОТЛАДКА - UserBalanceManager.updateWalletBalance (70) - Обновление user..510978682

10 февр 2016 18: 15: 16987 - [бассейн-11-поточно-1] - ОТЛАДКА - SclwBalanceUpdater .updateBalance (51) - Итоговый баланс: 200.0

10 февраля 2016 18: 15: 16987 - [бассейн-11-поточно-1] - ОТЛАДКА - ScratchpadMasterStreamProcessor.processMessage (79) - Баланс обновления был успешным для бумажника 510978682

Сделка # 2

10 февраля 2016 18: 18: 19157 - [бассейн-11-поточно-1] - ИНФО - ConsumerThread .run (82) - Событие пОЛУЧАЛ

10 февраля 2016 18: 18: 19159 - [бассейн-11-поточно-1] - ОТЛАДКА - SclwBalanceUpdater.updateBalance (43) - Текущий баланс: 200.0

10 18 февраля 2016: 18: 19159 - [бассейн-11-поточно-1] - ОТЛАДКА - SclwBalanceUpdater.updateBalance (44) - Отклонение: 50,0

10 февраля 2016 18: 18: 19159 - [бассейн-11 -thread-1] - ОТЛАДКА - UserBalanceManager.updateWalletBalance (70) - Обновление user..510978682

10 февр 2016 18: 18: 19160 - [бассейн-11-поточно-1] - ОТЛАДКА - SclwBalanceUpdater.updateBalance (51) - Итоговый баланс: 250.0

10 февраля 2016 18: 18: 19160 - [бассейн-11-поточно-1] - ОТЛАДКА - ScratchpadMasterStreamProcessor.processMessage (79) - Баланс обновления был успешным для бумажник 510978682

сделка # 3 (Это потеряно)

10 февраля 2016 18: 18: 19160 - [бассейн-11-токарно-1] - INFO - ScratchpadMasterStreamProcessor.processMessage (62) - Печать ул идентификатор : 1466162182Scratchpad id: 9127117934

10 февраля 2016 18: 18: 19161 - [бассейн-11-поточно-1] - ОТЛАДКА - SclwBalanceUpdater.updateBalance (43) - Текущий баланс: 250.0

10 февраля 2016 18: 18: 19161 - [бассейн-11-поточно-1] - ОТЛАДКА - SclwBalanceUpdater.updateBalance (44) - Отклонение: -250,0

10 февраля 2016 18: 18: 19161 - [бассейн-11-поточно-1] - DEBUG - UserBalanceManager.updateWalletBalance (70) - Обновление пользователя..510978682

10 февраля 2016 18: 18: 19162 - [бассейн-11-поточно-1] - ОТЛАДКА - SclwBalanceUpdater.updateBalance (51) - Итоговый баланс: 0,0

10 февраля 2016 18: 18: 19162 - [бассейн -11-нить-1] - ОТЛАДКА - ScratchpadMasterStreamProcessor.processMessage (79) - Баланс обновление была успешной для бумажника 510978682

сделки #-чтения затхлого баланса, ой

10 февраля 2016 18: 18: 23140 - [pool-11-thread-1] - INFO - ConsumerThread.run (82) - Полученное событие

10 февраля 2016 18: 18: 23140 - [бассейн-11-поточно-1] - ИНФО - ScratchpadMasterStreamProcessor.processMessage (62) - Печать ул ID: 1466162730Scratchpad ID: 9127120830

10 февраля 2016 18: 18: 23141 - [бассейн-11-поточно-1] - ОТЛАДКА - SclwBalanceUpdater.updateBalance (43) - Текущий баланс: 250.0

10 февраля 2016 18: 18: 23141 - [бассейн-11-резьбовых 1] - DEBUG - SclwBalanceUpdater.updateBalance (44) - Отклонение: 200,0

10 февраля 2016 18: 18: 23141 - [бассейн-11-поточно-1] - ОТЛАДКА - UserBalanceManager.updateWalletBalance (70) - Обновление user..510978682

10 февраля 2016 18: 18: 23,142 - [бассейн-11-поточно-1] - ОТЛАДКА - SclwBalanceUpdater.updateBalance (51) - Итоговый баланс: 450.0

10 февраля 2016 18: 18: 23142 - [бассейн-11-поточно-1] - ОТЛАДКА - ScratchpadMasterStreamProcessor.ProcessMessage (79) - Баланс обновление было успешным для бумажника 510978682

Я установил уровень согласованности в LOCAL_QUORUM как для чтения и записи, а также три сервера Cassandra узла имеет одинаковое время (с помощью NTP). В чем может быть проблема?

+1

База данных, такая как Cassandra, имеет значительные преимущества, когда речь заходит о расширении, распределении по нескольким центрам обработки данных и доступности. Но это связано с ценой сокращения гарантий последовательности. И это, скорее всего, то, что вы испытываете. Я не уверен, что Кассандра является правильным выбором, если баланс должен быть точным при любых обстоятельствах. – Codo

ответ

0

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

Однако я хотел бы предложить решение, не переходящее в другую БД. Вы можете использовать тип счетчика для своего поля balance. Операция обновления счетчика работает по-разному. Он отправляет команде cassandra для увеличения/уменьшения поля по значению cirtain, поэтому у вас не будет проблемы с несогласованностью.

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

+0

На самом деле, баланс - это плавающая запятая, поэтому использование счетчиков не будет работать. Во втором случае это означало бы, что при чтении мне придется выполнять поиск в двух таблицах и сохранять маркер для отслеживания транзакции, которая не была агрегирована. Либо я удаляю много строк, либо моя вторая таблица становится огромной, и я думаю, что это не будет полезно, поскольку чтение будет замедляться. – user20507

+0

Просто умножьте его на 100 и сохраните его как int (например, в центах вместо долларов). Если это не соответствует вашим требованиям, прочитайте вторую часть моего предложения. – AlexR

+0

Ну, мне нужно, чтобы баланс всегда был правильным. Во-вторых, использование счетчика также не гарантирует, что устаревшие чтения будут предотвращены [ссылка] (http://stackoverflow.com/questions/20953821/how-to-rapidly-increment-counters-in-cassandra-w-o-staleness) – user20507

0

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

Укажите временные метки для запросов на обновление?

Временные метки позволяют вам предоставить правильную последовательность для операции мутации. Если вы не указали результат временной метки транзакции № 3, ее можно перезаписать транзакцией №2, потому что они выполняются быстро.

1

Кассандра очень хороша с неизменяемыми данными и идемпотентными операциями. Не так много с транзакциями или частыми обновлениями/удалениями.

One quick проверка если вы используете легкий сделка. Они приходят за производительность, но, возможно, необходимы в важных данных. Например,

UPDATE balances 
SET balance = <new_balance> 
WHERE id = <wallet_id> 
IF balance = <old_balance> 
Смежные вопросы