2013-04-30 3 views
0

Я работаю над приложением, которое хранит измерения датчиков. Иногда датчики отправляют ошибочные измерения (например, измеренное значение выходит за пределы). Мы не хотим сохранять каждую ошибку измерения отдельно, но мы хотим сохранить статистику об этих ошибках, такую ​​как идентификатор датчика, дату первой ошибки, дату последней ошибки и другую информацию, такую ​​как количество последовательных ошибок , который я здесь опустим ...Как хранить интервал дат в Кассандре?

Вот упрощенная версия класса "ErrorStatistic":

package foo.bar.repository; 

import org.joda.time.DateTime; 

import javax.annotation.Nonnull; 
import javax.annotation.Nullable; 

import static com.google.common.base.Preconditions.checkNotNull; 

public class ErrorStatistic { 

    @Nonnull 
    private final String sensorId; 
    @Nonnull 
    private final DateTime startDate; 
    @Nullable 
    private DateTime endDate; 

    public ErrorStatistic(@Nonnull String sensorId, @Nonnull DateTime startDate) { 
     this.sensorId = checkNotNull(sensorId); 
     this.startDate = checkNotNull(startDate); 
     this.endDate = null; 
    } 

    @Nonnull 
    public String getSensorId() { 
     return sensorId; 
    } 

    @Nonnull 
    public DateTime getStartDate() { 
     return startDate; 
    } 

    @Nullable 
    public DateTime getEndDate() { 
     return endDate; 
    } 

    public void setEndDate(@Nonnull DateTime endDate) { 
     this.endDate = checkNotNull(endDate); 
    } 

} 

я в настоящее время сохраняющиеся эти ErrorStatistic используя Гектора следующим образом:

private void persistErrorStatistic(ErrorStatistic errorStatistic) { 
    Mutator<String> mutator = HFactory.createMutator(keyspace, StringSerializer.get()); 

    String rowKey = errorStatistic.getSensorId(); 
    String columnName = errorStatistic.getStartDate().toString(YYYY_MM_DD_FORMATTER); 
    byte[] value = serialize(errorStatistic); 

    HColumn<String, byte[]> column = HFactory.createColumn(columnName, value, StringSerializer.get(), BytesArraySerializer.get()); 
    mutator.addInsertion(rowKey, COLUMN_FAMILY, column); 

    mutator.execute(); 
} 

private static final DateTimeFormatter YYYY_MM_DD_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd"); 

Когда мы получим первое измерение по ошибке, мы создаем ErrorStatistic с набором sensorId и startDate, а также нулем endDate. Этот ErrorStatistic хранится в нашей модели в памяти и сохраняется в Cassandra. Затем мы обновляем ErrorStatistic в памяти для следующих измерений по ошибке, пока не получим действительное измерение, после чего ErrorStatistic сохраняется и удаляется из нашей модели в памяти.

Таким образом, Cassandra содержит статистику ошибок с открытыми интервалами (например, [2012-08-01T00: 00Z | null]) и закрытые интервалы (например, [2012-08-01T00: 00Z | 2013-01-12T10: 23Z]).

Я хочу иметь возможность запрашивать данные статистики ошибок по дате.

Например, если у меня есть эти статистические данные 3 ошибки:

sensorId = foo 
startDate = 2012-08-01T00:00Z 
endDate = 2012-09-03T02:10Z 

sensorId = foo 
startDate = 2012-10-04T03:12Z 
endDate = 2013-02-01T12:28Z 

sensorId = foo 
startDate = 2013-03-05T23:22Z 
endDate = null 
(this means we have not received a valid measurement since 2013-03-05) 

Если я запрашиваю Кассандру с датой:

  • 2012-08-04T10: 00Z -> он должен вернуть сначала ErrorStatistic
  • 2012-09-04T00: 00Z -> он должен возвращать, что на данный момент ошибок не было
  • 2014-01-03T00: 00Z -> он должен вернуть последний ErrorStatistic (поскольку он открыт -конец ed)

Я не уверен, как я должен хранить и «индексировать» объекты ErrorStatistic, чтобы эффективно их запрашивать. Я совершенно новый для Кассандры, и я мог бы пропустить что-то очевидное.


Edit:. Следующий был добавлен в ответ на предложение Joost, что я должен сосредоточиться на тип запросов Я заинтересован в

у меня будет два типа запроса:

  • Первое, как вы догадались, - это перечислить всю ErrorStatistics для данного датчика и временного диапазона. Это кажется относительно легким. Единственная проблема, с которой я столкнулся, заключается в том, когда ErrorStatistics начинает до интересующего меня времени (например, я запрашиваю все ошибки за апрельские месяцы, и я хочу, чтобы мой запрос возвращал ErrorStatistics [2012-03-29: 2012-04-02] тоже ...)
  • Второй запрос кажется сложнее.Я хочу найти для данного датчика и даты ошибкуStatistics, чей интервал содержит указанную дату или чей startDate предшествует данной дате, с нулем endDate (это означает, что мы все еще получаем ошибки для этого датчика). Я не знаю, как это сделать эффективно. Я мог бы просто загрузить всю ErrorStatistics для данного датчика, а затем проверить интервалы в Java ... Но я бы хотел избежать этого, если это возможно. Наверное, я хочу, чтобы Cassandra начиналась с определенной даты и смотрела назад, пока не найдет первую ErrorStatistics с startDate, которая предшествует данной дате (если она есть), затем загрузите ее и проверьте на Java, если ее endDate - null или после указанной даты , Но я понятия не имею, возможно ли это, и насколько это эффективно.

ответ

1

Вопрос, который вы должны задать себе, - это вопросы, которые вы задаете для ErrorStatistics. Дизайн схемы Cassandra обычно начинается с подхода «Таблица для запроса». Не начинайте с данных (сущностей), которые у вас есть, но с вашими вопросами/запросами. Это другое мышление, чем «традиционный» дизайн rdbms, и я нашел, что требуется некоторое время, чтобы привыкнуть.

Например, вы хотите запросить статистику на датчик? Вместо решения может быть таблица с составным ключом (идентификатор датчика, timeuuid). Такая таблица позволяет быстро найти идентификатор датчика, сортируя результаты по времени.

Если вы хотите запросить статистику датчика только на основе времени, то (составная) клавиша с единицей времени может быть более полезной, возможно, с помощью окошечных элементов, чтобы лучше распределить нагрузку по узлам. Обратите внимание, что запросы catch: range на первичных ключах невозможны с использованием случайных или громоздких разделителей Cassandra. Существуют другие разделители, но они легко имеют тенденцию к неравномерному распределению нагрузки в вашем кластере.

Вкратце, начните с ответов, которые вы хотите, а затем сработайте «назад» к дизайну стола. С правильной схемой ваш код будет следовать.


Сложение (2013-9-5): Что хорошо знать, что Cassandra сортирует данные в пределах одного ключа секционирования. Это очень полезно. Например, измерения будут отсортированы по start_time в порядке (сначала новые) по убыванию, если вы определить таблицу, как:

create table SensorByDate 
(
    sensor_id uuid, 
    start_date datetime, 
    end_date datetime, 
    measurement int 
    primary key (sensor_id, start_date) 
) 
with clustering order by (start_time DESC); 

В этом примере sensor_id является ключом раздел и определяет узел эта строка хранится на. Start_date - это второй элемент в составном ключе и определяет порядок сортировки.

Чтобы получить первое измерение после определенной даты начала в этой таблице можно сформулировать запрос как

select * from SensorByDate 
where sensor_id = ? and start_date < ? limit 1 
+0

Мне нравится ваша идея создать свою схему с помощью «Таблицы каждого запроса» подхода. Как новый пользователь Cassandra, потребуется некоторое время, чтобы привыкнуть к нему. Я собираюсь обновить свой вопрос, чтобы указать два типа запросов, которые меня будут интересовать. –

+0

Я добавил пример, который может помочь в решении вашей проблемы. –

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