基于TableStore的物联网元数据管理

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,恶意文件检测 1000次 1年
对象存储 OSS,内容安全 1000次 1年
简介: # 背景 常见的企业级无线接入方案有两种,分别被称作廋AP和胖AP。瘦AP(AC+AP)架构为比较传统的企业级无线接入方案,主要优点就是漫游体验好,但是AC宕机的话会导致所属的AP全部无法工作。对于大型的办公场所,漫游的需求相对较弱,新型的胖AP(无AC,不会因为AC宕机导致网络不可用)+ 云端控制器架构成为了新兴的一种企业无线接入方案,运维人员通过云端对AP进行监控与管理。

背景

常见的企业级无线接入方案有两种,分别被称作廋AP和胖AP。瘦AP(AC+AP)架构为比较传统的企业级无线接入方案,主要优点就是漫游体验好,但是AC宕机的话会导致所属的AP全部无法工作。对于大型的办公场所,漫游的需求相对较弱,新型的胖AP(无AC,不会因为AC宕机导致网络不可用)+ 云端控制器架构成为了新兴的一种企业无线接入方案,运维人员通过云端对AP进行监控与管理。

某公司拥有无线AP约10,000台、接入终端(STA)100,000个。设备以一定的周期上报其状态到云端,云端将监控数据持久化后供用户查看。

业务描述

每个AP设备会以10s的周期上报其当前状态,上报数据格式为json,其格式如下:
AP状态:

{
    "ap_mac": "11:22:33:86:D9:E8",  // AP WAN口MAC地址,AP设备唯一标识
    "report_time": 1532315501985,   // 上报时间戳,毫秒
    "on_time": 1531417181972,       // 设备上线时间戳,毫秒
    "sta_cnt": 2,                       // 终端数量
    "cpu_usage": 12,                // CPU使用率
    "memory_usage": 38,             // CPU使用率
    "wan_recv_speed": 280,          // WAN口下行速率 单位bps
    "wan_sent_speed": 45348,        // WAN口上行速率 单位bps
}

需求以及架构选型

需求

  1. 通过MAC地址查看每个AP最新的状态。
  2. 用户需要在管理系统上对基于各种条件对设备进行查询。
  3. 需要对AP的各种指标进行排序,以便找出故障设备。
    我们将上述的需求分为两种:
  4. 多维查询。
  5. 排序。
    基于这两大类需求,我们给出如下的架构选型比较。

架构选型

针对这种IOT场景的设备状态监控数据,下面针对几种常见方案做比较。

MySQL

将设备上报的状态数据直接写入MySQL,并使用MySQL自带的查询、排序语句对数据进行分析,这种架构最为简单,用户运维成本较低。

这种架构仅仅适用于小规模量的数据,在大规模数据的情况下,MySQL的内部架构也导致了无法创建出一种万用的索引来满足多维查询的需求。并且MySQL底层使用的是B+数作为存储结构,会有随机写的问题,写入性能较差。
MySQL在使用前必须指定表结构,也就是说后续新增需求的话,必须要修改表结构,在数据量较大的情况下修改表结构很容易造成锁表导致线上故障。

MySQL + 自建Elasticsearch

由于MySQL的检索能力较弱,MySQL + Elasticsearch也是业界比较常见的方案。用户将数据写入MySQL,并使用binlog订阅工具(如canal)将数据异步写入Elasticsearch,架构如下图所示:

image.png | left | 827x265

其中Canal Client需要用户自己编写与部署。相比单MySQL的架构,该方案很好地解决了MySQL在多维查询和指定列排序能力弱的问题。但是带了的问题也比较多:

  1. Canal与Elasticsearch需要用户自己部署,带来的运维成本也相对提升。
  2. Canal Client侧负责读取Canal传输过来MySQL增量改变数据,数据的一致性是需要用户自己保证的。

使用表格存储的SearchIndex功能

表格存储底层存储使用的LSM模型,很好地解决了MySQL写入性能差的问题,特别适合IOT这种写多读少的场景。

用户将数据写入表格存储后系统内部会将数据异步同步到SearchIndex,数据写入TableStore到数据可查约有毫秒到秒级别的延迟,用户无需关注运维相关的问题,数据一致性也有系统内部保证,做到了开箱即用。

结论

基于上面的比较,表格存储更适合存储AP的状态数据,并且通过SearchIndex可以很容易地完成多维查询以及排序。简明的系统整体架构如下图所示:

image.png | left | 597x73

表结构设计

表格存储底层使用主键的第一列将数据均分到对应的分区上,以达到负载均衡的目的。我们知道MAC地址的前3个字节为厂商码,也就是说如果同一个厂家生产出来的设备MAC地址前3个字节大多会是相同的,如果直接使用MAC地址做主键的话可能会导致数据热点,所以我们推荐对MAC地址做MD5之后做第一列主键。关于表结构设计的最佳实践详见这里

最新状态数据

AP状态

表名:wifi_ap_status

列类型 列名 类型 示例 备注
主键列 pk0 String 1b5de627b4a25553baf1f72af9afb96d MD5(ap_mac),对ap_mac做MD5
值列 ap_mac String 11:22:33:44:55:66 AP MAC地址
report_time Integer​ 1537363646533 UTC时间戳,毫秒
on_time I​nteger 1537363646533 同上
sta_cnt I​nteger 10 所连接终端数
cpu_usage I​nteger 20 CPU使用率
memory_usage I​nteger 50 内存使用率
wan_recv_speed I​nteger 817 收数据速率,单位bps
wan_sent_speed I​nteger 2411 发数据速率,单位bps

代码示例

下面将以AP状态作为例子,给出全流程的代码示例。

初始化

创建TableStore client

SyncClient syncClient = new SyncClient(
                    "$endpoint",
                    "$accessKeyId",
                    "$accessKeySecret",
                    "$instanceName"
            );

SyncClient对象为线程安全,如果使用Spring的话可以将其作为一个单例Bean注入到其他对象中使用

创建TableStore表

表的创建可以在控制台上完成,也可以通过SDK完成,如果使用SDK的话代码示例如下

创建AP状态表

// 指定表名
TableMeta tableMeta = new TableMeta("wifi_ap_status");
// 指定主键列,根据上面的表结构设计,这边只有pk0一个主键列
tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema("pk0", PrimaryKeyType.STRING));
CreateTableRequest createTableRequest = new CreateTableRequest(tableMeta, new TableOptions(-1, 1));
syncClient.createTable(createTableRequest);

创建SearchIndex

与创建表相同,SearchIndex的创建可以通过控制台完成,如果使用SDK的话,示例如下:

创建AP状态SearchIndex

CreateSearchIndexRequest createSearchIndexRequest = new CreateSearchIndexRequest();
createSearchIndexRequest.setIndexName("wifi_ap_status");
createSearchIndexRequest.setTableName("wifi_ap_status");
IndexSchema indexSchema = new IndexSchema();
indexSchema.setIndexSetting(new IndexSetting(5));
indexSchema.setFieldSchemas(Arrays.asList(
        new FieldSchema("ap_mac", FieldType.TEXT).setIndex(true), // 可搜索
        new FieldSchema("report_time", FieldType.LONG).setIndex(true).setEnableSortAndAgg(true), // 可搜索并可排序
        new FieldSchema("sta_cnt", FieldType.LONG).setIndex(true).setEnableSortAndAgg(true),
        new FieldSchema("cpu_usage", FieldType.LONG).setIndex(true).setEnableSortAndAgg(true),
        new FieldSchema("memory_usage", FieldType.LONG).setIndex(true).setEnableSortAndAgg(true)
));
createSearchIndexRequest.setIndexSchema(indexSchema);
CreateSearchIndexResponse resp = syncClient.createSearchIndex(createSearchIndexRequest);

数据写入

用户只需使用原表格存储的写入功能将数据写入即可,表格存储内部会自动将数据导入SearchIndex,无需关心内部实现。

PutRowRequest putRowRequest = new PutRowRequest();
RowPutChange rowPutChange = new RowPutChange("wifi_ap_status");

String apMac = "11:22:33:86:D9:E8";
// 通过AP MAC计算MD5,防止产生数据热点,这边使用了apache的commons-codec库
String pk0 = DigestUtils.md5Hex(apMac);
PrimaryKey pk = new PrimaryKey(new PrimaryKeyColumn[]{
    new PrimaryKeyColumn("pk0", PrimaryKeyValue.fromString(pk0))
});

rowPutChange.setPrimaryKey(pk);
rowPutChange.addColumns(new Column[]{
        new Column("ap_mac", ColumnValue.fromString(apMac)),
        new Column("report_time", ColumnValue.fromLong(System.currentTimeMillis())),
        new Column("on_time", ColumnValue.fromLong(System.currentTimeMillis())),
        new Column("cpu_usage", ColumnValue.fromLong(56)),
        new Column("sta_cnt", ColumnValue.fromLong(4)),
        new Column("memory_usage", ColumnValue.fromLong(43)),
        new Column("wan_recv_speed", ColumnValue.fromLong(280)),
        new Column("wan_sent_speed", ColumnValue.fromLong(45348)),
});

putRowRequest.setRowChange(rowPutChange);


syncClient.putRow(putRowRequest);

数据读取

数据读取分为两种:
1.基于原生的表格存储的主键获取
2.基于SearchIndex功能获取
下面对于这两种不通模式的读取分别举例说明

通过主键读取

通过主键获取AP状态的话是直接从表格存储的表中直接获取的。也就是说,在通过主键获取数据的时候是不需要通过SearchIndex功能的,代码示例如下:

GetRowRequest getRowRequest = new GetRowRequest();
String apMac = "11:22:33:86:D9:E8";
// 通过AP MAC计算MD5,防止产生数据热点,这边使用了apache的commons-codec库
String pk0 = DigestUtils.md5Hex(apMac);
// 设置主键
PrimaryKey pk = new PrimaryKey(new PrimaryKeyColumn[]{
        new PrimaryKeyColumn("pk0", PrimaryKeyValue.fromString(pk0))
});

SingleRowQueryCriteria singleRowQueryCriteria = new SingleRowQueryCriteria("wifi_ap_status", pk);
singleRowQueryCriteria.setMaxVersions(1);
getRowRequest.setRowQueryCriteria(singleRowQueryCriteria);

GetRowResponse rowResponse = syncClient.getRow(getRowRequest);
Row row = rowResponse.getRow();
// 获取主键列
PrimaryKey primaryKey = row.getPrimaryKey();
for (PrimaryKeyColumn primaryKeyColumn : primaryKey.getPrimaryKeyColumns()) {
    System.out.println("PrimaryKeyColumn:(" + primaryKeyColumn.getName() + ":" + primaryKeyColumn.getValue() + ")");
}
// 获取值列
for (Column column : row.getColumns()) {
    System.out.println("Column:(" + column.getName() + ":" + column.getValue() + ")");
}

通过SearchIndex功能读取

为了方便描述,下面通过SQL(仅为表示具体需求,SearchIndex暂不支持SQL语句)+代码的形式给出示例来描述我们的场景。

多维查询

如果需要通过非主键列进行多维查询,我们可以使用syncClient的search方法,在上面的例子中,我们为wifi_ap_status表创建了SearchIndex,并且指定了索引列。
如果要实现下面的SQL:

SELECT
*
FROM  wifi_ap_status
WHERE ap_mac LIKE '%86:D9:E8%' AND sta_cnt >= 2

用java语言实现的话,代码如下

SearchQuery searchQuery = new SearchQuery();
// 使用BoolQuery来实现组合条件查询,本例搜索了ap_mac包含86:D9:E8并且sta_cnt大于等于2的数据
BoolQuery query = new BoolQuery();
// 使用短语搜索模糊匹配ap_mac
MatchPhraseQuery macQuery = new MatchPhraseQuery();
macQuery.setFieldName("ap_mac");
macQuery.setText("86:D9:E8");
// 使用范围查询sta_cnt
RangeQuery staCntQuery = new RangeQuery();
staCntQuery.setFieldName("sta_cnt");
staCntQuery.setFrom(ColumnValue.fromLong(2), true);
query.setMustQueries(Arrays.asList(
        macQuery,
        staCntQuery
));
searchQuery.setQuery(query);

// 构建搜索请求
SearchRequest searchRequest = new SearchRequest(
        "wifi_ap_status", // 表格存储表名
        "wifi_ap_status", // SearchIndex索引名
        searchQuery
);
// 设置需要返回的表列
SearchRequest.ColumnsToGet columnsToGet = new SearchRequest.ColumnsToGet();
// 设置返回所有列
columnsToGet.setReturnAll(true);
searchRequest.setColumnsToGet(columnsToGet);

// 搜索请求
SearchResponse searchResponse = syncClient.search(searchRequest);
List<Row> rows = searchResponse.getRows();

for (Row row : rows) {
    PrimaryKey primaryKey = row.getPrimaryKey();
    for (PrimaryKeyColumn primaryKeyColumn : primaryKey.getPrimaryKeyColumns()) {
        System.out.println("PrimaryKeyColumn:(" + primaryKeyColumn.getName() + ":" + primaryKeyColumn.getValue() + ")");
    }

    for (Column column : row.getColumns()) {
        System.out.println("Column:(" + column.getName() + ":" + column.getValue() + ")");
    }
}

排序

排序功能也是我们的常见需求,比如我们需要查看在某个条件下挂载终端数最多的几个AP,如果用SQL语句描述的话如下:

SELECT
*
FROM  wifi_ap_status
WHERE ap_mac LIKE '%11:22:33%'
ORDER BY sta_cnt DESC

如果用代码表示的话,如下:

SearchQuery searchQuery = new SearchQuery();
// 使用短语搜索模糊匹配ap_mac
MatchPhraseQuery macQuery = new MatchPhraseQuery();
macQuery.setFieldName("ap_mac");
macQuery.setText("11:22:33");
searchQuery.setQuery(macQuery);

// 排序选项,sta_cnt降序
FieldSort staCntSorter = new FieldSort("sta_cnt");
staCntSorter.setOrder(SortOrder.DESC);

searchQuery.setSort(new Sort(Collections.singletonList(
        staCntSorter
)));

// 构建搜索请求
SearchRequest searchRequest = new SearchRequest(
        "wifi_ap_status",
        "wifi_ap_status",
        searchQuery
);
// 设置需要返回的表列
SearchRequest.ColumnsToGet columnsToGet = new SearchRequest.ColumnsToGet();
// 设置返回所有列
columnsToGet.setReturnAll(true);
searchRequest.setColumnsToGet(columnsToGet);

// 搜索请求
SearchResponse searchResponse = syncClient.search(searchRequest);
List<Row> rows = searchResponse.getRows();
目录
相关文章
|
3月前
|
供应链 NoSQL 物联网
链接全球数十亿台设备!物联网行业如何应对数据管理、实时分析和供应链优化的挑战?
物联网已成为面向未来的解决方案的关键组成部分,且其所蕴含的巨大经济价值潜力有待挖掘
1440 0
链接全球数十亿台设备!物联网行业如何应对数据管理、实时分析和供应链优化的挑战?
|
8月前
|
存储 消息中间件 监控
Tablestore 物联网存储全面升级 -- 分析存储公测
物联网存储功能介绍随着物联网技术的快速发展,物联网已广泛应用于制造业、能源、建筑、医疗、交通、物流仓储等多个领域,物联网的应用能够有效节约资源、提高效率、保障安全以及降低成本,帮助各行业实现可持续发展目标。在物联网场景中根据数据特点进行分类,数据主要包括设备元数据、设备消息数据和设备时序数据三种类型,不同类型数据的存储需求不同。物联网场景中不同类型数据的存储核心需求如下:设备元数据:主要数据为设备
211 0
Tablestore 物联网存储全面升级 -- 分析存储公测
|
传感器 存储 数据采集
谈谈如何管理物联网数据以及数据管理策略
任何物联网体系中都有两个关键组成部分就是是设备和数据。
谈谈如何管理物联网数据以及数据管理策略
|
存储 消息中间件 SQL
基于 EMQX + Tablestore 打造车辆元数据管理平台
车辆网场景中的云端架构分享与案例实践。
645 0
|
存储 SQL 消息中间件
基于物联网平台 + Tablestore,如何打造设备元数据管理平台?
基于物联网平台 + Tablestore,如何打造设备元数据管理平台?
206 0
基于物联网平台 + Tablestore,如何打造设备元数据管理平台?
|
存储 SQL NoSQL
基于物联网平台 + Tablestore 打造设备元数据管理平台
从场景到实践,分享物联网设备元数据场景的业务特点、技术选型和案例实践。
368 0
|
存储 SQL 传感器
基于 Tablestore 时序存储的物联网数据存储方案
背景物联网时序场景是目前最火热的方向之一。海量的时序数据如汽车轨迹数据、汽车状态监控数据、传感器实时监控数据需要存放进入数据库。一般这类场景下存在如下需求数据高写入,低读取需要对写入数据进行基础的图表展示对写入数据进行聚合分析传统的关系型数据库并不适合此类场景,时序数据库脱颖而出。表格存储时序实例支持时序数据的存储,其具有如下特点:Serverless,分布式,低成本高写入支持优秀的索引能力对数据
1433 0
基于 Tablestore 时序存储的物联网数据存储方案
|
存储 运维 NoSQL
基于Tablestore的一站式物联网存储解决方案-场景篇
## 前言 随着5G时代的来临,万物互联概念的兴起,物联网渐渐覆盖到了各行各业中。本系列文章将为大家介绍基于表格存储Tablestore的一站式物联网存储解决方案。以共享充电宝场景为例,实现物联网场景下元数据、时序数据存储,高并发更新、分析计算等需求。 ## 背景 共享经济是近年来兴起的一种概念,共享概念极大方便了人们的生活。例如共享单车、共享车位、共享充电宝等等。这些场景里包含了大量的设备元
998 0
|
存储 运维 负载均衡
基于Tablestore的一站式物联网存储解决方案-表设计篇
## 前言 本章节主要讲解表格存储Tablestore的实例、表的创建步骤和共享充电宝场景的数据表设计。 ​ ## 表设计 ### 表功能设计 这里按照功能需求分为三张数据表:元数据表、订单数据表、元数据时序表 - 元数据表 元数据表保存了机柜的最新状态数据。用户租借、归还充电宝,以及运维人员上下线机柜,都会更新元数据表记录。在元数据表上建立索引,可提供多维查询的能力。对元数据表按照字段进行
718 0
|
SQL 存储 运维
基于Tablestore的一站式物联网存储解决方案-数据操作篇
## 前言 上一章节介绍了共享充电宝场景的表结构设计。本章节主要为大家介绍如何使用表格存储Tabelstore数据表实现基本数据读写、批量更新,以及利用多元索引特性实现多维度查询功能。 ## 准备工作 - 测试数据说明 | 数据表 | 数据表名 | 数据行数 | 说明 | | --- | --- | --- | --- | | 元数据表 | cabinet | 一千万行 | 模拟一千万台机柜 |
443 0