我有一个Cassandra问题。你知道Cassandra如何更新/增加计数器吗?
我想使用风暴螺栓(来自github上的storm-contrib repo的CassandraCounterBatchingBolt)写入cassandra。但是,我不确定incrementCounterColumn()方法的某些实现是如何工作的..而且cassandra计数器(来自:http://wiki.apache.org/cassandra/Counters)也存在局限性,这使得它对我的场景恕我没用:
如果写入意外失败(超时或失去与协调器节点的连接),客户端将不知道是否已执行该操作。重试可导致CASSANDRA-2495过度计数。
反移除本质上是有限的。例如,如果您非常快速地发出序列“增量,删除,增量”,则删除可能会丢失
无论如何,这是我的情景:
我更新相同的计数器比更新传播到其他Cassandra节点更快。
示例:
假设我有3个cassandra节点。每个节点上的计数器为0 节点1:0,节点2:0,节点3:0增量来:5 - >节点1:0,节点2:0,节点3:0
增量从节点2开始 - 仍然需要传播到node1和node3
节点1:0,节点2:5,节点3:0与此同时,另一个增量在前一个增量之前到达 传播:3 - >节点1:0,节点2:5,节点3:0
假设3个起始点与5个起点不同,我们有: 节点1:3,节点2:5,节点3:0
现在,如果3传播到其他节点AS AN INCREMENT而不是新值 (和5相同)然后最终节点都将等于8,这就是我想要的。
如果3覆盖5(因为它有一个更晚的时间戳),这是有问题的 - 不是我想要的。
你知道Cassandra如何处理这些更新/增量吗?
请注意,写入之前的读取仍然容易受到同一问题的影响,具体取决于读取执行的副本节点(如果传播距离不远,则仲裁仍然会失败)
我也在想,也许放一个缓存b / w我的风暴螺栓和Cassandra可以解决这个问题,但这是另一个时间的故事。
答案 0 :(得分:17)
C *中的计数器具有复杂的内部表示,可以避免在无领导分布式系统中计算事物的大多数(但不是全部)问题。我喜欢将它们视为分片计数器。计数器由许多由主机ID和版本号标识的子计数器组成。接收计数器操作的主机仅增加其自己的子计数器,并且还增加版本。然后它将其整个计数器状态复制到其他副本,并将其与其状态合并。读取计数器时,处理读操作的节点通过将每个主机的总计数相加来确定计数器值。
在每个节点上,计数器增量就像Cassandra中的其他所有内容一样,只是一个写入。增量将写入memtable,并通过合并memtable和所有SSTable中的所有增量在读取时确定本地值。
我希望当我说你不必担心增加计数器的速度比Cassandra能够处理时更快。由于每个节点都保留自己的计数器,并且永远不会复制增量操作,因此不会出现因读取 - 修改 - 写入方案引入的竞争条件而导致计数丢失的可能性。如果Cassandra接受了写作,你几乎可以保证它会被计算在内。
但是,你不能保证,除非你的计数在任何时候都是正确的。如果一个增量被写入一个节点,但是刚刚从另一个节点读取的计数器值,则无法保证增量已被复制,您还必须考虑网络分区期间会发生什么。这与Cassandra中的任何写入大致相同,它最终是一致的,它取决于您用于操作的一致性级别。
也有可能失去确认。如果你做了一个增量并且松散了与Cassandra的连接,然后你才能得到回复,你就无法知道你的写作是否得到了。当你得到连接时,你也无法分辨,因为在增加之前你不知道计数是多少。对于选择可用性而不是一致性的系统而言,这是一个固有的问题,以及您为许多其他好处付出的代价。
最后,快速删除,增量,删除的问题是真实的,你应该避免的事情。问题是增量操作基本上会恢复列,如果这些操作彼此足够接近,它们可能会得到相同的时间戳。 Cassandra是严格的最后写入胜利,并根据操作的时间戳确定最后一次。如果两个操作具有相同的时间戳,则“更大”的操作将获胜,这意味着以严格的字节顺序对其进行排序。这是真的,但我不会太担心它,除非你正在快速写入和删除相同的值(这可能是你的数据模型中的一个错误)。
这是Cassandra计数器内部的一个很好的指南:http://www.datastax.com/wp-content/uploads/2011/07/cassandra_sf_counters.pdf
答案 1 :(得分:3)
当前版本的计数器不适合用于需要保证不会过度计数和立即一致性的用例。
有增量和减量操作,这些操作不会相互冲突,除非发生任何丢失的突变或重放突变,否则会给你一个正确的结果。
重写Cassandra计数器(https://issues.apache.org/jira/browse/CASSANDRA-6504)可能对你很有意思,它应该解决所有当前关注的问题。
与此同时,如果我必须在当前版本的Cassandra上实现它,并且准确的计数是必不可少的,我可能会将每个增量或减量存储为一列,并对结果进行读取时间聚合,同时写回检查点,这样您就不必回读到开始时间来计算后续结果。
这给读取方增加了很多负担,尽管它在写入路径上非常有效,因此它可能适用于您的用例,也可能不适用。
答案 2 :(得分:2)
要了解更新/增量,即写入操作,我建议您阅读Cassandra用于通信的Gossip协议。在Gossip中,每个参与者(节点)使用元组σ(K) = (V*N)
维护其状态,其中σ(K)
是K
键的状态,V
值和N
作为版本号。
为了保持数据包的单一版本的真实性,Gossip保持了一个和解机制,即Precise
& Scuttlebutt
(电流)。根据{{1}},在更新任何元组之前,它们彼此通信以检查谁持有密钥的最高版本(最新值)。持有最高版本的人负责写操作。
有关详细信息,请阅读此article。