Cassandra

时间:2016-02-15 07:29:10

标签: java cassandra updates datastax consistency

我在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)
) 

群集中的总节​​点数:本地DC中的3个 复制因子:2 Cassandra版本:2.1.8

每次用户通过读取先前设置的值,添加交易金额并发布更新时,我都会更新列“余额”的值。我正在使用Java,Datastax驱动程序(2.1.5)。

在大约五十万笔交易中,一次特定更新将失败。这通常发生在用户快速连续完成两个事务时,直到几毫秒。这是日志:

  

交易#1

     

2016年2月10日18:15:16,984 - [pool-11-thread-1] - 信息 -   ScratchpadMasterStreamProcessor.processMessage(62) - 打印str id:   1466140282Scratchpad id:9127013322

     

2016年2月10日18:15:16,986 - [pool-11-thread-1] - DEBUG -   SclwBalanceUpdater.updateBalance(43) - 当前余额:0.0

     

2016年2月10日18:15:16,986 - [pool-11-thread-1] - DEBUG -   SclwBalanceUpdater.updateBalance(44) - 偏差:200.0

     

2016年2月10日18:15:16,986 - [pool-11-thread-1] - DEBUG -   UserBalanceManager.updateWalletBalance(70) - 正在更新user..510978682

     

2016年2月10日18:15:16,987 - [pool-11-thread-1] - DEBUG -   SclwBalanceUpdater.updateBalance(51) - 最终余额:200.0

     

2016年2月10日18:15:16,987 - [pool-11-thread-1] - DEBUG -   ScratchpadMasterStreamProcessor.processMessage(79) - 余额更新   成功的钱包510978682

     

交易#2

     

2016年2月10日18:18:19,157 - [pool-11-thread-1] - 信息 -   ConsumerThread.run(82) - 收到的事件

     

2016年2月10日18:18:19,159 - [pool-11-thread-1] - DEBUG -   SclwBalanceUpdater.updateBalance(43) - 当前余额:200.0

     

2016年2月10日18:18:19,159 - [pool-11-thread-1] - DEBUG -   SclwBalanceUpdater.updateBalance(44) - 偏差:50.0

     

2016年2月10日18:18:19,159 - [pool-11-thread-1] - DEBUG -   UserBalanceManager.updateWalletBalance(70) - 正在更新user..510978682

     

2016年2月10日18:18:19,160 - [pool-11-thread-1] - DEBUG -   SclwBalanceUpdater.updateBalance(51) - 最终余额:250.0

     

2016年2月10日18:18:19,160 - [pool-11-thread-1] - DEBUG -   ScratchpadMasterStreamProcessor.processMessage(79) - 余额更新   成功的钱包510978682

     

交易#3(遗失了)

     

2016年2月10日18:18:19,160 - [pool-11-thread-1] - 信息 -   ScratchpadMasterStreamProcessor.processMessage(62) - 打印str id:   1466162182Scratchpad id:9127117934

     

2016年2月10日18:18:19,161 - [pool-11-thread-1] - DEBUG -   SclwBalanceUpdater.updateBalance(43) - 当前余额:250.0

     

2016年2月10日18:18:19,161 - [pool-11-thread-1] - DEBUG -   SclwBalanceUpdater.updateBalance(44) - 偏差:-250.0

     

2016年2月10日18:18:19,161 - [pool-11-thread-1] - DEBUG -   UserBalanceManager.updateWalletBalance(70) - 正在更新user..510978682

     

2016年2月10日18:18:19,162 - [pool-11-thread-1] - DEBUG -   SclwBalanceUpdater.updateBalance(51) - 最终余额:0.0

     

2016年2月10日18:18:19,162 - [pool-11-thread-1] - DEBUG -   ScratchpadMasterStreamProcessor.processMessage(79) - 余额更新   成功的钱包510978682

     

交易#4阅读陈旧的余额,哎呀

     

2016年2月10日18:18:23,140 - [pool-11-thread-1] - 信息 -   ConsumerThread.run(82) - 收到的事件

     

2016年2月10日18:18:23,140 - [pool-11-thread-1] - 信息 -   ScratchpadMasterStreamProcessor.processMessage(62) - 打印str id:   1466162730Scratchpad id:9127120830

     

2016年2月10日18:18:23,141 - [pool-11-thread-1] - DEBUG -   SclwBalanceUpdater.updateBalance(43) - 当前余额:250.0

     

2016年2月10日18:18:23,141 - [pool-11-thread-1] - DEBUG -   SclwBalanceUpdater.updateBalance(44) - 偏差:200.0

     

2016年2月10日18:18:23,141 - [pool-11-thread-1] - DEBUG -   UserBalanceManager.updateWalletBalance(70) - 正在更新user..510978682

     

2016年2月10日18:18:23,142 - [pool-11-thread-1] - DEBUG -   SclwBalanceUpdater.updateBalance(51) - 最终余额:450.0

     

2016年2月10日18:18:23,142 - [pool-11-thread-1] - DEBUG -   ScratchpadMasterStreamProcessor.processMessage(79) - 余额更新   成功的钱包510978682

我已将读取和写入的一致性级别设置为LOCAL_QUORUM,并且三个cassandra节点服务器具有相同的时间(使用NTP)。可能是什么问题?

3 个答案:

答案 0 :(得分:1)

Cassandra非常善于使用不可变数据和幂等操作。与交易或频繁更新/删除无关。

如果您使用轻量级交易,请快速检查一下。它们的性能成本很高,但在重要数据中可能是必需的。例如,

UPDATE balances
SET balance = <new_balance>
WHERE id = <wallet_id>
IF balance = <old_balance>

答案 1 :(得分:0)

首先,请看一下Codo的评论,该评论很好地描述为什么你有问题。

但是,我想建议一个解决方案而不转移到其他数据库。 您可以为balance字段使用计数器类型。计数器的更新声明的工作方式不同。它发送到cassandra命令以通过cirtain值增加/减少字段,因此您不会有不一致的问题。

然而,基于计数器的解决方案并不适用于所有应用程序。例如,它仅限于整数类型。可能更常见的解决方案是以编程方式创建一种事务:在单独的表中保存更新请求,并创建异步过程,该过程聚合在特定时间段内完成的所有更新请求,并将它们应用于balance值。

答案 2 :(得分:0)

您似乎有更新操作的竞争条件。 Cassandra不会更新数据,但会插入一个带有另一个时间戳的新数据。具有上一个时间戳的数据是数据的实际版本。

您是否指定了更新查询的时间戳?

时间戳允许您为变异操作提供正确的序列。 如果您没有指定Transaction#3的时间戳结果,那么它可以被Transaction#2覆盖,因为它们是快速连续完成的。