2010-08-24 6 views
8

Я бы хотел использовать Cassandra для хранения счетчика. Например, сколько раз просматривалась данная страница. Счетчик никогда не будет уменьшаться. Значение счетчика не обязательно должно быть точным, но оно должно быть точным с течением времени.Как увеличить счетчик в Кассандре?

Моя первая мысль состояла в том, чтобы сохранить значение в виде столбца и просто прочитать текущий счетчик, увеличить его на единицу, а затем вернуть обратно. Однако, если другая операция также пытается увеличить счетчик, я думаю, что конечное значение будет просто с самой последней отметкой времени.

Другая мысль заключалась в том, чтобы хранить каждую загрузку страницы как новый столбец CF. Затем я мог просто запустить get_count() на этом ключе и получить количество столбцов. Считываясь через documentation, кажется, что это не очень эффективная операция.

Я неправильно подхожу к проблеме?

+0

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

+0

любая удача с URL-адресом, который я вам дал сегодня сегодня? – Schildmeijer

+1

Именно это я и предложил с помощью get_count(). Возможно, лучший способ сделать это - хранить их в CF как столбцы, запускать get_count() и кэшировать его в «счетчике», который обновляется на любой частоте, которую требует мое приложение. –

ответ

5

Счетчики были добавлены к Кассандре 0,8

Использование метода Incr увеличиваем значение колонки 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 потоков управляет каждым осколком как отдельный счетчик. Вы можете рандомизировать, какой поток используется вашим приложением каждый раз для балансировки нагрузки без атак по этим потокам. Просто убедитесь, что каждый поток отвечает за один осколок.

3a) (Лучше всего) Используйте отдельный инструмент, который является транзакционным (он же РСУБД) или поддерживает операции атомарного инкремента (memcached, redis).

[Update.2] Я бы не использовал распределенную блокировку (см. Мьютексы memcached и zookeeper), так как это очень несовместимо с отказом узла или сетевым разделением, если оно неправильно реализовано.

2

Что я в итоге сделал, это использовать get_count() и кэшировать результат в кешировании ColumnFamily.

Таким образом, я мог бы получить общее предположение по счету, но все равно получаю точный счет, когда захочу.

Кроме того, мне удалось настроить, насколько устаревшие данные были готовы принять по запросу.

1

Мы собираемся решить аналогичную проблему, сохранив текущее значение счетчика в распределенном кеше (например, memcached). Когда счетчик будет обновлен, мы сохраним его значение в Cassandra. Поэтому, даже если какой-то узел кэша завершится неудачно, мы сможем получить значение из базы данных.

Это решение не является совершенным.Однако данные такого счетчика посещений не очень чувствительны, поэтому, по моему мнению, допускаются незначительные несоответствия.

0

Интересно, что я не вижу, чтобы кто-нибудь упоминал о возможности подсчета на основе каждого компьютера. Скажем, ваше приложение работает на 5 машинах с именем a1, a2, ... a5. Затем вы можете иметь блокировку для каждой машины (т. Е. Файл, который вы открываете с помощью 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.)

Таким образом, можно избежать блокировки, которая блокирует все узлы и все же вы все еще получаете безопасный/последовательный подсчет (очевидно, для чтения + сумма не является совершенной, и она дает только приблизительную сумму, но она по-прежнему остается непротиворечивой.)

Я не слишком уверен, указал ли Бен Бернс на наличие n осколков и n потоков То же самое, но это звучит не так, как мне.

И с 0.8.x вы можете использовать счетчики Cassandra, что, безусловно, намного проще сделать, хотя это может не всегда соответствовать вашим потребностям.

Смежные вопросы