基于TableStore的物联网元数据管理
背景
常见的企业级无线接入方案有两种,分别被称作廋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
}
需求以及架构选型
需求
- 通过MAC地址查看每个AP最新的状态。
- 用户需要在管理系统上对基于各种条件对设备进行查询。
- 需要对AP的各种指标进行排序,以便找出故障设备。
我们将上述的需求分为两种: - 多维查询。
- 排序。
基于这两大类需求,我们给出如下的架构选型比较。
架构选型
针对这种IOT场景的设备状态监控数据,下面针对几种常见方案做比较。
MySQL
将设备上报的状态数据直接写入MySQL,并使用MySQL自带的查询、排序语句对数据进行分析,这种架构最为简单,用户运维成本较低。
这种架构仅仅适用于小规模量的数据,在大规模数据的情况下,MySQL的内部架构也导致了无法创建出一种万用的索引来满足多维查询的需求。并且MySQL底层使用的是B+数作为存储结构,会有随机写的问题,写入性能较差。
MySQL在使用前必须指定表结构,也就是说后续新增需求的话,必须要修改表结构,在数据量较大的情况下修改表结构很容易造成锁表导致线上故障。
MySQL + 自建Elasticsearch
由于MySQL的检索能力较弱,MySQL + Elasticsearch也是业界比较常见的方案。用户将数据写入MySQL,并使用binlog订阅工具(如canal)将数据异步写入Elasticsearch,架构如下图所示:
其中Canal Client需要用户自己编写与部署。相比单MySQL的架构,该方案很好地解决了MySQL在多维查询和指定列排序能力弱的问题。但是带了的问题也比较多:
- Canal与Elasticsearch需要用户自己部署,带来的运维成本也相对提升。
- Canal Client侧负责读取Canal传输过来MySQL增量改变数据,数据的一致性是需要用户自己保证的。
使用表格存储的SearchIndex功能
表格存储底层存储使用的LSM模型,很好地解决了MySQL写入性能差的问题,特别适合IOT这种写多读少的场景。
用户将数据写入表格存储后系统内部会将数据异步同步到SearchIndex,数据写入TableStore到数据可查约有毫秒到秒级别的延迟,用户无需关注运维相关的问题,数据一致性也有系统内部保证,做到了开箱即用。
结论
基于上面的比较,表格存储更适合存储AP的状态数据,并且通过SearchIndex可以很容易地完成多维查询以及排序。简明的系统整体架构如下图所示:
表结构设计
表格存储底层使用主键的第一列将数据均分到对应的分区上,以达到负载均衡的目的。我们知道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 | Integer | 1537363646533 | 同上 | |
sta_cnt | Integer | 10 | 所连接终端数 | |
cpu_usage | Integer | 20 | CPU使用率 | |
memory_usage | Integer | 50 | 内存使用率 | |
wan_recv_speed | Integer | 817 | 收数据速率,单位bps | |
wan_sent_speed | Integer | 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();
原文链接
本文为云栖社区原创内容,未经允许不得转载。
基于TableStore的物联网元数据管理相关推荐
- 数据仓库与元数据管理
数据仓库与元数据管理 1. 前言 在事务处理系统中的数据,主要用于记录和查询业务情况.随着数据仓库(DW)技术的不断成熟,企业的数据逐渐变成了决策的主要依据.数据仓库中的数据是从许多业务处理系统中抽取 ...
- 业务元数据管理——洞悉数据背后的业务含义
本文转自微信号EAWorld.扫描下方二维码,关注成功后,回复"普元方法+",将会获得热门课堂免费学习机会! 目前,很多企业已经意识到,由于业务人员看不懂系统中存储的数据,所以难以 ...
- 基于TableStore的海量电商订单元数据管理
一.背景 订单系统存在于各行各业,如电商订单.银行流水.运营商话费账单等,是一个非常广泛.通用的系统.对于这类系统,在过去十几年发展中已经形成了经典的做法.但是随着互联网的发展,以及各企业对数据的重视 ...
- 基于TableStore的亿级订单管理解决方案
一.方案背景 订单系统存在于各行各业,如电商订单.银行流水.运营商话费账单等,是一个非常广泛.通用的系统.对于这类系统,在过去十几年发展中已经形成了经典的做法.但是随着互联网的发展,以及各企业对数据的 ...
- Alluxio基于冷热数据分离的元数据管理策略
文章目录 前言 Alluxio内部元数据管理架构 Alluxio的支持异步写出功能的自定义Cache实现 引用 前言 上篇文章末尾,笔者聊到了一种叫做分层元数据管理模式.它主张的思想是将元数据进行分级 ...
- 基于TableStore的海量气象格点数据解决方案实战
前言 气象数据是一类典型的大数据,具有数据量大.时效性高.数据种类丰富等特点.气象数据中大量的数据是时空数据,记录了时间和空间范围内各个点的各个物理量的观测量或者模拟量,每天产生的数据量常在几十TB到 ...
- mysql 轨迹数据存储_基于Tablestore实现海量运动轨迹数据存储-阿里云开发者社区...
前言 现在越来越多的人都开始关心自己的运动数据,比如每日的计步.跑步里程.骑行里程等.运动APP与运动类的穿戴设备借助传感器.地图.GPS定位等技术,收集好运动数据以后,通过与互联网社交功能结合,产生 ...
- 基于Tablestore的Wifi设备监管系统架构实现
Wifi设备监管 某知名跨国公司,在全球范围内拥有大量园区,园区内会有不同部门的同事在一起办公.每个园区内都要配备大量的Wifi设备从而为园区同事提供方便的上网服务.因此,集团需要一套完善的监管系统维 ...
- 基于Tablestore的Wifi设备监管系统架构实现 1
Wifi设备监管 某知名跨国公司,在全球范围内拥有大量园区,园区内会有不同部门的同事在一起办公.每个园区内都要配备大量的Wifi设备从而为园区同事提供方便的上网服务.因此,集团需要一套完善的监管系统维 ...
最新文章
- Codeigniter文件上传类型不匹配错误
- java swt webkit_使用Java和Webkit的HTML编辑器 – SWT浏览器
- IT-标准化-系列-6.关闭事件跟踪程序
- 中国最险六大寺庙,最后一座至今无人登临
- *【HDU - 1517】【POJ - 2505】A Multiplication Game(博弈,递推找规律或SG函数)
- bootstrap checkbox_[推荐]icheck-bootstrap(漂亮的ckeckbox/radiobox)
- win10+Linux双系统安装
- flash物理引擎应用:FisixObject类(1)
- python文本风格_Python的代码风格
- 关于wireshark中“TCP segment of a reassembled PDU”
- VFP全面控制EXCEL(转自十豆三老师)
- 管理手记:和上司相处不要太“学生气”
- 博客园添加导航目录(转)
- 安装quartusⅡ简明教程
- John破解Linux哈希密码文件
- Excel上传,加密,解析
- 深度linux触摸板失灵,deepin下触摸板无法使用
- LM13丨形态量化-动量周期分析
- 【ARM-Linux开发】嵌入式操作系统上的小型数据库移植SQLite
- Word 如何让页眉显示各个章节名字
热门文章
- 一加到1亿。C语言_可能是今年最难选的2部手机:小米10详细对比一加8T
- python 支付宝个人账单_金融支付财务融合业务-实践分享1:订单、账单、交易流水、账套知识解构、原理解析...
- 【LeetCode笔记】剑指 Offer 16. 数值的整数次方(Java、分治)
- python抽荣耀水晶_教你2种免费拿荣耀水晶的方法,获奖概率让人惊喜,一般人我不告诉他...
- android fragment 管理器,Android Fragment 與 Fragment管理器
- java电脑类的接口_java 一个类实现两个接口的案例
- gcn代码pytorch_GCN的简单实现(pytorch)
- 天才王垠惊人言论炸翻网友:相对论是假说,爱因斯坦是民科!
- 如果唐朝人拿到一篇现代文章……
- 当我们群嘲假博士时,不要忘了真博士们的艰辛