在 Clickhouse 中存储传感器数据的最佳方式是什么?
What is the best way to store sensor data in Clickhouse?
我们有一套设备,它们都有传感器。所有设备都有一些通用的传感器,但有些设备有额外的传感器。每个传感器都有不同的离散化水平,有些传感器有时变化很快,有时一段时间内不能变化。
例如,我们有 DeviceA 并且有一个形式的数据包流(NULL 表示值不变):
Timestamp, Temp, Latitude, Longitude, Speed...
111, 20, 54.111, 23.111, 10
112, 20, NULL, NULL, 13
113, 20, NULL, 23.112, 15
和设备B:
Timestamp, Temp, Latitude, Longitude, Speed..., AdditionalSensor
111, 24, 54.111, 23.121, 10 ... 1
112, 23, 55.111, 23.121, 13 ... 2
113, 23, 55.111, 23.122, 15 ... 1
一段时间后,可以将新传感器添加到某些设备。
每个传感器都可以是任何数字类型(Int32、UInt8、Float32)
之后数据会被用来计算:dau, mau, retention, GPS coordinates clustering等等。
我们可以简单地创建一些 table:
CREATE TABLE Sensors
(
Date Date,
Id FixedString(16),
DeviceNumber FixedString(16),
TimeUtc DateTime,
DeviceTime DateTime,
Version Int32,
Mileage Int32,
Longitude Float64,
Latitude Float64,
AccelX Float64,
AccelY Float64,
AccelZ Float64
...
) ENGINE = MergeTree(Date, (DeviceNumber, TimeUtc), 8192);
但这里有两个问题:不支持一组不同的传感器,有时我们在某些传感器值没有变化的情况下有空值,很高兴看到时间戳之前的最后一个非空值。
我们可以通过创建一个 table 字段来解决第一个问题:SensorName、Timestamp、Date、Value。但是如何选择正确的类型呢?我们应该为不同的类型使用不同的 table 吗?
可能我们需要使用石墨引擎,不幸的是,我对此没有任何经验。所以非常感谢任何帮助。如果能够仅保留任何传感器的更改值,那就太好了。
更新
我找到了一种处理空值的方法。我们可以使用 "anyLast" 函数来请求列的最后接收值:
SELECT anyLast(Lights) FROM test where TimeUtc <= toDateTime('2017-11-07 11:13:59');
遗憾的是,我们无法使用某种重叠 window 函数(clickhouse 不支持它们)来填充所有缺失值。因此,在可为空字段的情况下,聚合函数将仅使用非空值,在不可为空字段的情况下,将使用包括零值在内的所有值,这两种方式都是不正确的。解决方法是在使用 select 插入之前填充空值,并为一行中的所有空值使用 anyLast 值。
您可以像使用时间序列数据库一样使用 Clickhouse。
您的 table 定义限制您使用动态指标。这就是您尝试处理 NULL 值的原因。
您可以将此 table 用于传感器值:
CREATE TABLE ts1(
entity String,
ts UInt64, -- timestamp, milliseconds from January 1 1970
s Array(String), -- names of the sensors
v Array(Float32), -- sensor values
d Date MATERIALIZED toDate(round(ts/1000)), -- auto generate date from ts column
dt DateTime MATERIALIZED toDateTime(round(ts/1000)) -- auto generate date time from ts column
) ENGINE = MergeTree(d, entity, 8192)
这里我们正在加载设备 A 的传感器值:
INSERT INTO ts1(entity, ts, s, v)
VALUES ('deviceA', 1509232010254, ['temp','lat','long','speed'], [24, 54.111, 23.121, 11])
正在查询 deviceA 临时数据:
SELECT
entity,
dt,
ts,
v[indexOf(s, 'temp')] AS temp
FROM ts1
WHERE entity = 'deviceA'
┌─entity─┬──────────────────dt─┬────────────ts─┬─temp─┐
│ deviceA│ 2017-10-28 23:06:50 │ 1509232010254 │ 24 │
└────────┴─────────────────────┴───────────────┴──────┘
检查 以获得详细用法。
我们有一套设备,它们都有传感器。所有设备都有一些通用的传感器,但有些设备有额外的传感器。每个传感器都有不同的离散化水平,有些传感器有时变化很快,有时一段时间内不能变化。 例如,我们有 DeviceA 并且有一个形式的数据包流(NULL 表示值不变):
Timestamp, Temp, Latitude, Longitude, Speed...
111, 20, 54.111, 23.111, 10
112, 20, NULL, NULL, 13
113, 20, NULL, 23.112, 15
和设备B:
Timestamp, Temp, Latitude, Longitude, Speed..., AdditionalSensor
111, 24, 54.111, 23.121, 10 ... 1
112, 23, 55.111, 23.121, 13 ... 2
113, 23, 55.111, 23.122, 15 ... 1
一段时间后,可以将新传感器添加到某些设备。 每个传感器都可以是任何数字类型(Int32、UInt8、Float32)
之后数据会被用来计算:dau, mau, retention, GPS coordinates clustering等等。
我们可以简单地创建一些 table:
CREATE TABLE Sensors
(
Date Date,
Id FixedString(16),
DeviceNumber FixedString(16),
TimeUtc DateTime,
DeviceTime DateTime,
Version Int32,
Mileage Int32,
Longitude Float64,
Latitude Float64,
AccelX Float64,
AccelY Float64,
AccelZ Float64
...
) ENGINE = MergeTree(Date, (DeviceNumber, TimeUtc), 8192);
但这里有两个问题:不支持一组不同的传感器,有时我们在某些传感器值没有变化的情况下有空值,很高兴看到时间戳之前的最后一个非空值。
我们可以通过创建一个 table 字段来解决第一个问题:SensorName、Timestamp、Date、Value。但是如何选择正确的类型呢?我们应该为不同的类型使用不同的 table 吗? 可能我们需要使用石墨引擎,不幸的是,我对此没有任何经验。所以非常感谢任何帮助。如果能够仅保留任何传感器的更改值,那就太好了。
更新
我找到了一种处理空值的方法。我们可以使用 "anyLast" 函数来请求列的最后接收值:
SELECT anyLast(Lights) FROM test where TimeUtc <= toDateTime('2017-11-07 11:13:59');
遗憾的是,我们无法使用某种重叠 window 函数(clickhouse 不支持它们)来填充所有缺失值。因此,在可为空字段的情况下,聚合函数将仅使用非空值,在不可为空字段的情况下,将使用包括零值在内的所有值,这两种方式都是不正确的。解决方法是在使用 select 插入之前填充空值,并为一行中的所有空值使用 anyLast 值。
您可以像使用时间序列数据库一样使用 Clickhouse。
您的 table 定义限制您使用动态指标。这就是您尝试处理 NULL 值的原因。
您可以将此 table 用于传感器值:
CREATE TABLE ts1(
entity String,
ts UInt64, -- timestamp, milliseconds from January 1 1970
s Array(String), -- names of the sensors
v Array(Float32), -- sensor values
d Date MATERIALIZED toDate(round(ts/1000)), -- auto generate date from ts column
dt DateTime MATERIALIZED toDateTime(round(ts/1000)) -- auto generate date time from ts column
) ENGINE = MergeTree(d, entity, 8192)
这里我们正在加载设备 A 的传感器值:
INSERT INTO ts1(entity, ts, s, v)
VALUES ('deviceA', 1509232010254, ['temp','lat','long','speed'], [24, 54.111, 23.121, 11])
正在查询 deviceA 临时数据:
SELECT
entity,
dt,
ts,
v[indexOf(s, 'temp')] AS temp
FROM ts1
WHERE entity = 'deviceA'
┌─entity─┬──────────────────dt─┬────────────ts─┬─temp─┐
│ deviceA│ 2017-10-28 23:06:50 │ 1509232010254 │ 24 │
└────────┴─────────────────────┴───────────────┴──────┘
检查