智能家居场景下的数据建模示例

本文档为“时序数据建模”章节的第四篇。YMatrix 认为,数据模型的设计会直接影响到数据消费与使用的价值。因此,除技术介绍外,我们尝试通过整个章节使你对时序数据模型(Time-series Data Model)的概念、应用及发展都有清晰的理解。

  • 首篇为“时序数据模型是什么?”,通过回答几个层层深入的问题,最终使你对时序数据模型概念本身有清晰的理解。
  • 第二篇“时序建模思路”将尝试从理论指导的角度给出 YMatrix 关系模型设计思路参考。
  • 第三、四篇为车联网场景及智能家居场景(即此文档)下的数据建模示例,以“时序建模思路”为指导,给出 YMatrix 中不同时序场景的建模最佳实践。

注意!
此篇仅为参考示例,正式设计建模前建议至少完整详读 YMatrix 架构及组件原理。组件原理见“参考指南”章节。

1 什么是智能家居?

智能家居是物联网(IoT,Internet of Things)的典型应用场景之一。智能家居(Smart Home, Home Automation)是以住宅为平台,利用综合布线技术、网络通信技术、安全防范技术、自动控制技术、音视频技术将家居生活有关的设施集成,构建高效的住宅设施与家庭日程事务的管理系统,提升家居安全性、便利性、舒适性、艺术性,并实现环保节能的居住环境。

2 智能家居建模最佳实践

可以设想一下,假如你是一个智能空调指标平台 B 的开发/运维人员。现在,你想要就此平台的业务在 YMatrix 中进行建模设计,可能的思路如下:

序号 步骤
1 需求调研
2 建模设计与实施
3 模型测试

2.1 需求调研

  • 智能空调指标平台 B 的数据结构:

    其中品类、产品、设备及指标,组成每个指标值数据点的唯一标识。品类、产品、设备是空调指标表的静态标签集(Tagset),指标即为动态指标名。

  • 智能空调指标平台 B 的数据特征:

    • 数据规模:产品数量 10w 级别,每个产品出产数量不等的出货设备,一般为 100w ~ 1000w 级别,指标数量基本在 100 个以内。
    • 产品指标:同一品类产品的指标类似但不完全一样。
    • 指标类型:不确定。
  • 智能空调指标平台 B 的查询特点:

    • 最新值查询:某一个产品的某个设备的某一个指标的最新值。
    • 单产品聚集查询:某一个产品对应的所有设备在某一天的指标和。
  • 完整数据流: 产品(传感器)端预计算、明细数据 -> 云端 -> YMatrix -> 智能家居应用程序

结论:经过仔细、全面的前期调研,我们认为在 YMatrix 中,此智能家居场景数据指标前期设计阶段难以确定,数据规模、设备数量大,应使用窄表模型,以常见的 textfloat 为基础类型,并选择压缩性能良好的 MARS2 存储引擎为基础建表。

2.2 建模设计与实施

2.2.1 表结构

按不同的指标数据类型来构建多张窄表。

=# CREATE TABLE public.iot_float (
    product_type text, 
    product text,
    device text,
    metric text,
    value float4,
    ts timestamp with time zone
)
USING MARS2 
DISTRIBUTED BY (product_type,product,device) 
PARTITION BY range(ts)
(
 START ('2023-01-15') INCLUSIVE
 END ('2023-01-22') EXCLUSIVE
 EVERY (interval '1 hour'),
 DEFAULT PARTITION default_p
);
=# CREATE INDEX ON public.iot_float USING mars2_btree (device,ts,product_type,product);
=# CREATE TABLE public.iot_text (
    product_type text, 
    product text,
    device text, 
    metric text,
    value text,
    ts timestamp with time zone
)
USING MARS2 
DISTRIBUTED BY (product_type,product,device) 
PARTITION BY range(ts)
(
 START ('2023-01-15') INCLUSIVE
 END ('2023-01-22') EXCLUSIVE
 EVERY (interval '1 hour'),
 DEFAULT PARTITION default_p
);
=# CREATE INDEX ON public.iot_text USING mars2_btree (device,ts,product_type,product);

此示例中:

  • 标签为 product_type,product,device 三列;
  • 指标为 metric 列,指标值为 value 列;
  • 指标类型为 float,text 两种;
  • 以 product_type,product,device 作为分布键;
  • 以 device,ts,product_type,product 作为排序键;
  • 以 ts 作为分区键;
  • 2023-01-152023-01-22 每小时为一个分区。

2.2 模型测试

序号 测试计划 SQL 语句
1 查看最新 10 条数据 SELECT * FROM <表名> ORDER BY ts DESC LIMIT 10
2 查询总行数 SELECT COUNT(*) FROM <表名>
3 查询某一设备最新上报的全部指标 SELECT * FROM <表名> WHERE <设备标签列名> = '<设备标签值>' ORDER BY <时间戳列名> DESC LIMIT 1
...

向 iot_float 表中插入 100 条测试语句。

=# INSERT INTO public.iot_float (product_type, product, device, metric, value, ts)
SELECT
    CASE (random() * 3)::int
        WHEN 0 THEN 'sensor'
        WHEN 1 THEN 'actuator'
        WHEN 2 THEN 'gateway'
        ELSE 'others'
    END AS product_type,
    'product_' || (random() * 10 + 1)::int AS product,
    'device_' || (random() * 100 + 1)::int AS device,
    CASE (random() * 5)::int
        WHEN 0 THEN 'temperature'
        WHEN 1 THEN 'humidity'
        WHEN 2 THEN 'pressure'
        WHEN 3 THEN 'voltage'
        WHEN 4 THEN 'current'
        ELSE 'others'
    END AS metric,
    random() * 100 AS value,
    timestamp '2023-01-15 00:00:00+00' + (random() * (timestamp '2023-01-22 00:00:00+00' - timestamp '2023-01-15 00:00:00+00')) AS ts
FROM generate_series(1, 100);

向 iot_text 表中插入 100 条测试语句。

=# INSERT INTO public.iot_text (product_type, product, device, metric, value, ts)
SELECT
    CASE (random() * 3)::int
        WHEN 0 THEN 'sensor'
        WHEN 1 THEN 'actuator'
        WHEN 2 THEN 'gateway'
        ELSE 'others'
    END AS product_type,
    'product_' || (random() * 10 + 1)::int AS product,
    'device_' || (random() * 100 + 1)::int AS device,
    CASE (random() * 5)::int
        WHEN 0 THEN 'status'
        WHEN 1 THEN 'message'
        WHEN 2 THEN 'error'
        WHEN 3 THEN 'warning'
        WHEN 4 THEN 'log'
        ELSE 'others'
    END AS metric,
    'value_' || (random() * 1000 + 1)::int AS value,
    timestamp '2023-01-15 00:00:00+00' + (random() * (timestamp '2023-01-22 00:00:00+00' - timestamp '2023-01-15 00:00:00+00')) AS ts
FROM generate_series(1, 100);

测试结果:

  1. 查看 iot_float 表的最新 10 条数据
    =# SELECT * FROM iot_float ORDER BY ts DESC LIMIT 10;
    product_type |  product   |  device   |   metric    |   value    |              ts
    --------------+------------+-----------+-------------+------------+-------------------------------
    actuator     | product_3  | device_24 | others     |  38.504875 | 2023-01-21 18:38:55.188439+08
    others      | product_10 | device_59 | voltage     |  35.519894 | 2023-01-21 18:11:00.799357+08
    sensor       | product_11 | device_15 | temperature |  74.955025 | 2023-01-21 17:04:56.706237+08
    others      | product_7  | device_93 | pressure    |  62.177837 | 2023-01-21 14:52:45.494772+08
    gateway      | product_5  | device_1  | humidity    | 0.23014386 | 2023-01-21 14:51:14.290224+08
    others      | product_6  | device_61 | voltage     |  46.473114 | 2023-01-21 13:55:51.427582+08
    gateway      | product_4  | device_50 | pressure    |  53.413925 | 2023-01-21 10:11:41.670681+08
    actuator     | product_9  | device_13 | pressure    |  24.377035 | 2023-01-21 09:09:19.720012+08
    gateway      | product_3  | device_26 | current     |  58.887447 | 2023-01-21 07:38:12.482037+08
    gateway      | product_8  | device_82 | pressure    |  76.386024 | 2023-01-21 06:14:41.306756+08
    (10 rows)

    查看 iot_text 表的最新 10 条数据

    =# SELECT * FROM iot_text ORDER BY ts DESC LIMIT 10;
    product_type |  product   |  device   | metric  |   value   |              ts
    --------------+------------+-----------+---------+-----------+-------------------------------
    gateway      | product_10 | device_70 | status  | value_933 | 2023-01-21 22:03:51.85526+08
    others      | product_10 | device_46 | status  | value_471 | 2023-01-21 21:27:29.235879+08
    actuator     | product_4  | device_65 | log     | value_72  | 2023-01-21 21:20:27.515338+08
    gateway      | product_1  | device_34 | error   | value_864 | 2023-01-21 20:17:05.031651+08
    gateway      | product_6  | device_79 | warning | value_681 | 2023-01-21 18:03:51.699731+08
    actuator     | product_6  | device_92 | message | value_463 | 2023-01-21 17:51:20.218522+08
    gateway      | product_8  | device_44 | log     | value_526 | 2023-01-21 17:28:13.374733+08
    actuator     | product_4  | device_2  | warning | value_78  | 2023-01-21 16:52:34.770392+08
    sensor       | product_7  | device_49 | log     | value_258 | 2023-01-21 10:40:49.467672+08
    actuator     | product_7  | device_36 | error   | value_981 | 2023-01-21 08:05:55.809313+08
    (10 rows)
  2. 查询 iot_float 表的总行数
    =# SELECT COUNT(*) FROM iot_float;
    count
    -------
    100
    (1 row)

    查询 iot_text 表的总行数

    =# SELECT COUNT(*) FROM iot_text;
    count
    -------
    100
    (1 row)
  3. 查询 iot_float 表中某一设备最新上报的全部指标
    =# SELECT * FROM iot_float WHERE device = 'device_1' ORDER BY ts DESC LIMIT 1;
    product_type |  product  |  device  |  metric  |   value    |              ts
    --------------+-----------+----------+----------+------------+-------------------------------
    gateway      | product_5 | device_1 | humidity | 0.23014386 | 2023-01-21 14:51:14.290224+08
    (1 row)

    查询 iot_text 表中某一设备最新上报的全部指标

    =# SELECT * FROM iot_text WHERE device = 'device_66' ORDER BY ts DESC LIMIT 1;
    product_type |  product  |  device   | metric |  value   |              ts
    --------------+-----------+-----------+--------+----------+-------------------------------
    others      | product_5 | device_66 | error  | value_94 | 2023-01-20 20:00:48.991428+08
    (1 row)

注意!
在此只给出了简单的测试示例,更多场景测试示例见图形化界面 轻松上手板块。在实际环境中,请根据具体的查询需求设计具体的测试语句。