我正在开发一个存储传感器测量值的应用程序。有时,传感器将发送错误的测量值(例如,测量值超出范围)。我们不希望单独保留每个测量错误,但我们希望保留有关这些错误的统计信息,例如传感器ID,第一个错误的日期,上一个错误的日期以及其他信息,例如连续错误的数量,我在这里省略......
以下是“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);
}
}
我目前使用Hector持久化这些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");
当我们错误地收到第一个测量值时,我们会创建一个Error sensorId
和startDate
设置的ErrorStatistic,以及null endDate
。此ErrorStatistic保存在我们的内存模型中,并保存在Cassandra中。
然后,我们更新内存中的ErrorStatistic,以便进行下一次错误测量,直到我们收到有效的测量结果,此时ErrorStatistic将被保留并从我们的内存模型中删除。
Cassandra因此包含具有开放式间隔的ErrorStatistics(例如[2012-08-01T00:00Z | null])和封闭间隔(例如[2012-08-01T00:00Z | 2013-01-12T10:23Z])
我希望能够按日期查询这些ErrorStatistics。
例如,如果我有这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)
如果我用日期查询Cassandra:
我不确定如何存储和“索引”这些ErrorStatistic对象,以便有效地查询它们。我对Cassandra很陌生,我可能会遗漏一些明显的东西。
编辑:添加以下内容是为了回应Joost的建议,即我应该关注我感兴趣的查询类型。
我将有两种类型的查询:
startDate
在给定日期之前,为空endDate
(这意味着我们仍然收到错误对于这个传感器)。我不知道如何有效地做到这一点。我可以加载给定传感器的所有ErrorStatistics,然后检查Java中的间隔...但是如果可能的话我想避免这种情况。我想我希望Cassandra在给定的日期开始并向后看,直到它找到第一个Error startDate
在给定日期之前的ErrorStatistics(如果有的话),然后加载它并检查Java是否{{1} }是endDate
或在给定日期之后。但我不知道这是否可行,以及效率如何。答案 0 :(得分:1)
您必须问自己的问题是您对ErrorStatistics有什么问题。 Cassandra架构设计通常以“每个查询表”方法开头。不要从您拥有的数据(实体)开始,而是从您的问题/查询开始。这与“传统”rdbms设计不同,我发现需要一些时间来习惯。
例如,您要查询每个传感器的统计信息吗?比具有复合键的表(传感器ID,timeuuid)可能是一种解决方案。这样的表允许每个传感器id快速查找,根据时间对结果进行排序。
如果您只想基于时间查询传感器统计信息,那么带有时间单位的(复合)密钥可能会更有帮助,可能使用分片元素来更好地在节点上分配负载。请注意,存在catch:使用Cassandra随机或杂音分区器对主键进行范围查询是不可行的。还有其他分区程序,但它们容易导致群集中负载分布不均匀。
简而言之,从您想要的答案开始,然后“向后”工作到您的桌面设计。使用适当的架构,您的代码将遵循。
Addition(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