【问题标题】:how to rapidly increment counters in Cassandra w/o staleness如何在没有陈旧性的情况下快速增加 Cassandra 中的计数器
【发布时间】:2014-01-24 02:01:35
【问题描述】:

我有一个 Cassandra 问题。你知道 Cassandra 是如何更新/增加计数器的吗?

我想使用一个写入 cassandra 的风暴螺栓(来自 github 上的storm-contrib repo 的 CassandraCounterBatchingBolt)。但是,我不确定 incrementCounterColumn() 方法的一些实现是如何工作的......而且 cassandra 计数器(来自:http://wiki.apache.org/cassandra/Counters)也存在限制,这使得它们对我的场景恕我直言无用:

  • 如果写入意外失败(超时或失去与协调节点的连接),客户端将不知道操作是否已执行。重试可能会导致 CASSANDRA-2495 计数过多。

  • 计数器移除本质上是有限的。例如,如果您非常快速地发出“递增、删除、递增”序列,则删除可能会丢失

无论如何,这是我的场景:
我更新同一个计数器的速度比更新传播到其他 Cassandra 节点的速度要快。

示例
假设我有 3 个 cassandra 节点。每个节点上的计数器都是 0。
节点1:0,节点2:0,节点3:0

一个增量来了:5 -> Node1:0, node2:0, node3:0

增量从节点 2 开始——仍然需要传播到节点 1 和节点 3
节点1:0,节点2:5,节点3:0

与此同时,另一个增量在前一个增量之前到达
传播:3 -> Node1:0, node2:5, node3:0

假设 3 从与 5 开始的节点不同的节点开始:
节点1:3,节点2:5,节点3:0

现在如果 3 作为增量而不是作为新值传播到其他节点 (对于 5 也是一样)然后最终节点都等于 8,这就是我想要的。

如果 3 覆盖 5(因为它具有较晚的时间戳),这是有问题的 - 这不是我想要的。

您知道 Cassandra 如何处理这些更新/增量吗?

请注意,写入之前的读取仍然容易受到相同问题的影响,具体取决于读取从哪个副本节点执行(如果传播距离不远,Quorum 仍然可能失败)

我也在想,也许在我的风暴螺栓和 Cassandra 中放置一个缓存可能会解决这个问题,但那是另一个故事了。

【问题讨论】:

    标签: cassandra distributed apache-storm


    【解决方案1】:

    C* 中的计数器具有复杂的内部表示,可以避免大多数(但不是全部)在无领导分布式系统中计算事物的问题。我喜欢将它们视为分片计数器。一个计数器由许多由主机 ID 和版本号标识的子计数器组成。接收计数器操作的主机只增加它自己的子计数器,并且也增加版本。然后它将其整个计数器状态复制到其他副本,这些副本将其与它们的状态合并。当读取计数器时,处理读取操作的节点通过汇总来自每个主机的总计数来确定计数器值。

    在每个节点上,计数器增量就像 Cassandra 中的其他所有内容一样,只是一次写入。增量写入 memtable,在读取时通过合并 memtable 和所有 SSTable 中的所有增量来确定本地值。

    当我说您不必担心计数器的递增速度超过 Cassandra 的处理速度时,我希望您的解释能帮助您相信我。由于每个节点都有自己的计数器,并且从不复制增量操作,因此不会出现像 read-modify-write 场景那样的竞争条件而导致计数丢失的可能性。如果 Cassandra 接受写入,您几乎可以保证它会计数。

    但是,您不能保证计数始终正确,除非。如果增量写入一个节点,但计数器值随后从另一个节点读取,则不能保证增量已被复制,您还必须考虑在网络分区期间会发生什么。这与 Cassandra 中的任何写入或多或少相同,它具有最终一致的性质,并且取决于您用于操作的一致性级别。

    还有丢失确认的可能性。如果您在获得响应之前执行增量并断开与 Cassandra 的连接,您将无法知道您的写入是否成功。当您恢复连接时,您也无法判断,因为您不知道在增加之前计数是多少。对于选择可用性而不是一致性的系统来说,这是一个固有的问题,而且您要为许多其他好处付出代价。

    最后,快速删除、增量、删除的问题是真实存在的,应该避免。问题是增量操作本质上会复活列,如果这些操作彼此足够接近,它们可能会得到相同的时间戳。 Cassandra 是严格的 last-write-wins 并根据操作的时间戳确定最后一个。如果两个操作具有相同的时间戳,则“较大”的一个获胜,这意味着以严格的字节顺序排序的那个。这是真实的,但我不会太担心,除非您对相同的值进行非常快速的写入和删除(这可能是您的数据模型中的一个错误)。

    这是 Cassandra 计数器内部结构的一个很好的指南:http://www.datastax.com/wp-content/uploads/2011/07/cassandra_sf_counters.pdf

    【讨论】:

    • 感谢您的详尽解释。优秀的链接和帖子!是的,最后一个时间戳获胜的原因几乎就是我问这个问题的原因。现在,我知道有一个计数器的增量指令(而不是读+写)。 Is this true? 如果我迁移到云端,延迟会增加,为了避免在写入后立即读取错误的数字,我需要增加在保存到数据库之前聚合的时间。我希望计数器增量指令存在。
    • 有一个增量指令,你不必在增量之前进行读取。如果您使用一致性级别 QUORUM 进行增量并使用一致性级别 QUORUM 进行读取,则您永远不会看到计数不一致。
    • 我应该补充一点,在分区和崩溃等特殊情况下,当然存在计数过多或不足的可能性。
    • 更新:Cassandra 2.1(目前仍处于测试阶段)解决了当前共享计数器系统的许多弱点,并承诺提供更一致的性能。 datastax.com/dev/blog/…
    【解决方案2】:

    当前版本的计数器不适合需要保证不过度计数和即时一致性的用例。

    有递增和递减操作,它们不会相互冲突,并且除非有任何丢失的突变或重放的突变,否则会给你一个正确的结果。

    Cassandra 计数器 (https://issues.apache.org/jira/browse/CASSANDRA-6504) 的重写可能会让您感兴趣,它应该解决当前与获得正确计数有关的所有问题。

    同时,如果我必须在当前版本的 Cassandra 之上实现这一点,并且准确的计数是必不可少的,我可能会将每个增量或减量存储为一列,并对结果进行读取时聚合, 同时回写一个检查点,这样您就不必回读到开始时间来计算后续结果。

    这给读取端增加了很多负担,尽管它在写入路径上非常有效,因此它可能适用于您的用例,也可能不适用。

    【讨论】:

    • 读取仅用于在递增和写入之前读取新值;现在我可以增加我不需要先阅读,只需 inc;我确实执行读取,但每 30 分钟或 20 分钟一次,所以不是很频繁:)
    【解决方案3】:

    要了解更新/增量,即写入操作,我建议您通过 Gossip,Cassandra 用于通信的协议。在 Gossip 中,每个参与者(节点)使用元组 σ(K) = (V*N) 维护他们的状态,其中 σ(K)K 键的状态,V 值和 N 作为版本号。

    为了维护数据包真实的单一版本,Gossip 维护了一个协调机制,即Precise & Scuttlebutt(current)。根据Scuttlebutt Reconciliation,在更新任何元组之前,他们会相互通信以检查谁持有密钥的最高版本(最新值)。谁持有最高版本谁负责写操作。

    更多信息请阅读article

    【讨论】:

      猜你喜欢
      • 2015-12-25
      • 1970-01-01
      • 2017-11-29
      • 2011-03-18
      • 2017-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多