【问题标题】:How to increment a counter in Cassandra?如何在 Cassandra 中增加计数器?
【发布时间】:2012-08-31 02:32:43
【问题描述】:

我想用 Cassandra 来存储一个计数器。例如,给定页面被查看了多少次。 计数器永远不会递减。计数器的值不需要精确,但随着时间的推移应该是精确的。

我的第一个想法是将值存储为列并读取当前计数,将其递增 1,然后将其放回。但是,如果另一个操作也试图递增计数器,我认为最终值会只需成为具有最新时间戳的那个。

另一个想法是将每个页面加载作为新列存储在 CF 中。然后我可以在该键上运行get_count() 并获取列数。通读documentation,看来根本不是什么高效的操作。

我是不是错误地处理了这个问题?

【问题讨论】:

  • 我今天早些时候给你的 url 运气好吗?
  • 这正是我对 get_count() 的建议。也许最好的方法是将它们作为列存储在 CF 中,运行 get_count() 并将其缓存在一个“计数器”中,该计数器会根据我的应用程序需要的任何频率进行更新。
  • 通常我不会问这个问题,但是请考虑更改您接受的答案,因为技术已经发生了变化,并且 get_count 已被正式弃用。
  • 随着技术的变化改变了我选择的答案。

标签: cassandra


【解决方案1】:

计数器已添加到 Cassandra 0.8

使用 incr 方法将列的值增加 1。

[default@app] incr counterCF [ascii('a')][ascii('x')];
Value incremented.
[default@app] incr counterCF [ascii('a')][ascii('x')];
Value incremented.

在此处描述:http://www.jointhegrid.com/highperfcassandra/?p=79

或者它可以通过编程方式完成

CounterColumn counter = new CounterColumn();
ColumnParent cp = new ColumnParent("page_counts_by_minute");
counter.setName(ByteBufferUtil.bytes(bucketByMinute.format(r.date)));
counter.setValue(1);
c.add(ByteBufferUtil.bytes( bucketByDay.format(r.date)+"-"+r.url)
            , cp, counter, ConsistencyLevel.ONE);

此处描述:http://www.jointhegrid.com/highperfcassandra/?cat=7

【讨论】:

    【解决方案2】:

    [更新] 看起来计数器支持将在 0.8 的黄金时段准备好!

    我绝对不会使用 get_count,因为这是一个 O(n) 操作,每次读取“计数器”时都会运行。比它只是 O(n) 更糟糕的是,它可能跨越多个节点,这会引入网络延迟。最后,既然您只关心一个数字,为​​什么还要占用所有磁盘空间?

    目前,我根本不会使用 Cassandra 作为计数器。他们正在开发此功能,但尚未准备好迎接黄金时段。

    https://issues.apache.org/jira/browse/CASSANDRA-1072

    与此同时,您有几个选择。

    1)(不好)将您的计数存储在一条记录中,并让您的应用程序的一个且只有一个线程负责计数器管理。

    2) (更好)将计数器拆分为 n 个分片,并让 n 个线程将每个分片作为单独的计数器进行管理。您可以随机化您的应用每次使用哪个线程,以实现这些线程之间的无状态负载平衡。只需确保每个线程只负责一个分片即可。

    3a)(最佳)使用单独的工具,该工具可以是事务性的(也称为 RDBMS),也可以支持原子增量操作(memcached、redis)。

    [Update.2] 我会避免使用分布式锁(请参阅 memcached 和 zookeeper 互斥锁),因为如果实施不当,这对于节点故障或网络分区非常不容忍。

    【讨论】:

      【解决方案3】:

      我最终做的是使用 get_count() 并将结果缓存在缓存 ColumnFamily 中。

      这样我可以对计数进行大致猜测,但仍然可以随时获得准确的计数。

      此外,我能够根据每个请求调整我愿意接受的数据的陈旧程度。

      【讨论】:

        【解决方案4】:

        我们将通过将计数器的当前值保存在分布式缓存(例如 - memcached)中来解决类似的问题。当计数器更新时,我们会将其值存储在 Cassandra 中。因此,即使某个缓存节点发生故障,我们也能从数据库中获取值。

        这个解决方案并不完美。然而,像访问计数器这样的数据不是很敏感,所以我认为允许存在轻微的不一致。

        【讨论】:

          【解决方案5】:

          有趣的是,我没有看到任何人提到基于每台应用计算机的可能性。假设您的应用程序在名为 a1、a2、... a5 的 5 台机器上运行。然后,您可以在每台机器上锁定(即,您使用 O_EXCL 打开的文件或使用锁定等待其他实例使用计数器完成)并根据您的实现添加每台机器一行或一列。类似的东西

          machine_lock();
          this_column_family[machine-name][my-counter] += 1;
          machine_unlock();
          

          这样,每台机器就有一个计数器。当您需要总数时,您只需读取 a1、a2、... a5 并将它们相加即可。

          total = 0;
          foreach(machines as m) {
            total += this_column_family[m][my-counter];
          }
          

          (这是或多或少可以与libQtCassandra 一起使用的伪代码。)

          这样您可以避免锁定所有节点的锁,但您仍然可以获得安全/一致的计数(显然,读取 + 总和并不完美,它只为您提供总数的近似值,但仍然保持一致。)

          我不太确定 Ben Burns 所指出的关于拥有 n 个分片和 n 个线程的内容是否相同,但对我来说听起来并不完全一样。

          从 0.8.x 开始,您可以使用 Cassandra 计数器,这当然更容易操作,尽管它可能并不总是适合您的需求。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-06-08
            • 2016-09-11
            • 1970-01-01
            • 2023-03-08
            • 1970-01-01
            • 2018-03-22
            • 1970-01-01
            • 2015-10-12
            相关资源
            最近更新 更多