我在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)。可能是什么问题?
答案 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覆盖,因为它们是快速连续完成的。