2010-08-24 190 views
8

我想使用Cassandra来存储计数器。例如,查看给定页面的次数。 计数器永远不会减少。计数器的值不需要精确,但应该随着时间的推移准确无误。如何增加Cassandra中的计数器?

我的第一个想法是将该值存储为列,然后只读取当前计数,然后将其增加一个,然后将其重新放入。但是如果另一个操作也尝试增加计数器,我认为最终值只是最新的时间戳。

另一个想法是将每个页面加载存储为CF中的新列。然后我可以在该密钥上运行get_count()并获取列数。通过documentation阅读,它似乎并不是一个非常有效的操作。

我是否正确接近问题?

+0

http://maxgrinev.com/2010/07/12/update-idempotency-why-it-is-important-in-cassandra-applications-2/ – Schildmeijer 2010-08-24 17:41:25

+0

我今天早些时候给你的网址有什么好运? – Schildmeijer 2010-08-24 18:53:14

+1

这正是我用get_count()提出的建议。 也许最好的方法是将它们作为列存储在CF中,运行get_count()并将其缓存在一个“计数器”中,该计数器根据应用程序的需要更新频率。 – 2010-08-24 19:30:47

回答

5

柜台已被添加到卡桑德拉0.8

使用增量方法1。

[[email protected]] incr counterCF [ascii('a')][ascii('x')]; 
Value incremented. 
[[email protected]] 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

5

[更新]看起来像柜台支持将准备黄金时间在0.8!

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

就目前而言,我根本不会使用Cassandra作为计数器。他们正在研究这一功能,但尚未准备好黄金时段。

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

你得的平均时间几个选项。

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

2)(更好)将计数器拆分为n个碎片,并将n个线程作为单独的计数器管理每个碎片。您可以随意确定应用程序每次使用哪个线程,以实现跨这些线程的无状态负载平衡。只要确保每个线程都对一个分片负责。 (最好)使用一个单独的工具,它可以是事务性的(又名RDBMS)或支持原子增量操作(memcached,redis)的独立工具。

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

2

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

通过这种方式,我可以对计数进行一般性猜测,但每当我想要的时候仍然可以得到确切的计数。

此外,我能够调整过期数据,我愿意接受每个请求的基础上。

1

我们将通过将计数器的当前值保留在分布式缓存中来解决类似的问题(例如 - memcached)。当计数器更新时,我们将把它的值存储在Cassandra中。因此,即使某些缓存节点失败,我们也可以从数据库中获取值。

该解决方案并不完美。然而,这样的访问柜台的数据不是很敏感,所以我认为允许有微小的不一致。

0

有趣的是,我没有看到任何人提到可以依据每个应用程序计算机来计算。假设您的应用程序在名为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较少的工作。)

这样你可以避免锁定所有节点的锁,但你仍然可以得到安全/计数一致(当然读+ sum并不完美,它只给你一个近似的总数,但它仍然保持一致。)

我不太确定Ben Burns指出的有关n个碎片和n个线程是什么同样的事情,但它听起来不像我。

从0.8.x开始,您可以使用Cassandra计数器,虽然它可能并不总能满足您的需求,但确实很容易做到。