zoukankan      html  css  js  c++  java
  • 物联网的数据方案

    一、前言

    经常可以在科幻电影/CG中看到,某个指挥官,对着前面一个超大的数据大屏,指点江山。那个数据大屏,上面有着各项指标,以及汇总数据,通过各色各样的图形展示出来。
    从产品角度,指标与汇总条目的确定,决定了该数据大屏的价值。当然也可以支持自定义指标管理等。这不是本次的重点。
    从前端角度,如何利用最少的系统资源,将众多数据渲染出各个图表,则是重中之重。技术上,可以采用阿里AntV那一套等。这同样不是本次的重点。
    从后端角度,如何在满足数据请求内容的前提下,保证数据请求的高性能(低延迟,大吞吐量、高频次等),就是该场景下的要点。这才是本次的重点。

    二、背景

    作为物联网公司,需要向多个电力公司,进行数据大屏展示。其中数据大屏需要展示电力公司上百台风力发电机的状态,以及各项监控指标。其中监控指标中包含倾斜值、震动波形、结构应力等。而其中震动属于高频采集,频次都是上千Hz的。即使用于计算倾斜值的倾斜传感器,最低也是10/s。而监控大屏的数据,要求保证实时性。产品的要求是最好实时同步,最慢的指标也要保证5s一次的刷新率。最坑爹的,各个监控指标都需要可以查看历史数据,以及实时数据。

    如果按照震动数据来看,一条震动数据在数据库中的记录约为100字节,一个震动传感器每秒1000条记录,一台风机有4个震动传感器,一个电力公司当前最多有100台左右风机。这样一来,一家电力公司每秒有震动数据40W条记录,约为40M。震动是记录数最多的传感器,但不是带宽占用最大的类型,音视频占据的带宽更多。

    当然,这是没有任何处理的情况,所以看起来非常糟糕,前端完全无法处理。光震动就得每秒渲染40W数据,前端开发会杀了你的。尤其在历史数据的情况下,简直无法想象。

    别说这能不能渲染了。我都得怀疑数据库和带宽是否可以承受了。毕竟还有别的公司,别的场景,以及别的传感器。

    三、数据写入

    1.数据清洗

    首先,将一些明显不合规的数据,给清理掉。比如由于人为触碰导致的倾斜值跳动,人为作业导致的震动波动等。这部分过滤比例是很低的,主要是为了降噪。

    2.边缘计算

    风机的某项指标,往往是由多个传感器计算得到的。比如倾斜值,往往是通过一组(至少三个)传感器,计算得到的。
    所以,我们先需要在风机的边缘网关,进行协议解析,初始指标计算。
    这部分的处理,往往会将多个数据,转为一个数据记录。过滤效果还是不错的。

    PS:边缘计算,还应用在边缘网关的即时报警,以及机器关闭等操作。

    3.采样上传

    传感器的原始数据指标,是非常多的,一不留神就把硬盘打爆了,甚至是带宽。
    而其实往往我们并不需要这么多的原始数据指标。毕竟如果风力发电机要倒塌,也不会前一秒很正常,后一秒突然倒塌了。
    所以,我们需要对原始数据指标进行采样上传。并且需要对传感器采集频率进行确定。
    经过和业务、算法多方沟通&协商后,倾斜传感器的采样率定在了5%,应力传感器的采样率定在1%等。

    那么,传感器的采集频率和数据上传的采样率,有什么区别&联系呢?
    前者表示数据采集了,而且可能落盘了。后者表示对采集到的数据,抽取一部分,上传到物联网平台。
    那么,为什么不直接降低采集频率呢?
    一方面,部分传感器的采集频率是有其上下限的,不一定满足上传需求。另一方面,合理频率的原始数据,便于在发现问题后,进一步确定问题。
    所以,我们当时的处理,是原始采集数据直接本地磁盘顺序保存。并对原始数据进行采样,进行本地数据库保存,以及上传。

    4.特征值提取

    在某些垂直场景,我们需要计算出某些指标的特征值(平均值、方差),经过算法的计算,得出有关目标实体的结论。
    比如根据十分钟内的倾斜值平均值和方差,我就可以知道当前风机倾斜状况,并且通过方差,可以确定倾斜数据的稳定性。

    5.分级采集

    上面这一系列操作下来,数据已经过滤了七七八八。那么还有没有节省资源(功耗、存储、带宽等)的办法呢?
    正如上面提到的,风机就算要倒塌,也不会是一下子就倒塌了。所谓冰冻三尺,非一日之寒。咳咳,扯远了。也就是说,我们日常采集的监控数据大多数是无效的。所以为了提高资源利用率。在经过与业务、算法的沟通后,我这边提出采集等级概念。即风机所属传感器平时只保持低频采集状态,只有指标出现可疑情况,才会进入全功率状态。
    比如,震动传感器,由于无法降频与连续采样(因为震动的信息隐藏在连续的高频数据中),故其采集成本最高,所以设定为每天随机时间段连续采集10分钟。倾斜传感器每小时采集1分钟数据。当检测到可疑情况,如倾斜值超出目标阈值,则全功率状态。直到连续监测1小时,未出现可疑状况,则重新回归低频状态。

    其实,上述只有低频采集和全功率状态两个采集状态。其实可以扩展出多个采集级别。另外,还可以深入细化状态转换的触发条件等。

    通过分级采集,可以大大降低系统资源的浪费,却又保证了系统目标。何乐而不为呢。

    四、数据存储

    数据存储方面,只要关注物联网数据的特性:量大、有序、越是最近的,访问频率越高。方案上,多多考虑数据异构即可。

    1.存储方式

    简单来说,云平台的一条指标记录,会出现在四类存储上:缓存、数据库、统计表、归档表

    a.缓存

    由于刚刚插入的数据,经常被监控大屏、网页等显示终端展示,所以访问频次还是挺高的。尤其各类算法,经常需要扫描这些刚进来的萌新数据。
    首当其冲的就是缓存key的设计。我们当时的设计就是绑定关系(公司-场景-目标-指标)+时间戳。这样的查询,还是相当快捷的。
    其次就是失效时间的设定。我们当时的设定是1小时-1天。决定的标准是对应指标的访问频次和所占存储空间。
    最后就是一致性问题,由于这些数据都是不变数据,所以不存在一致性问题。

    不过缓存这边需要注意一点,就是需要将指标缓存与其他业务数据隔离。我们当时的设计,是放在不同的Redis集群。
    至于集群、可用性、持久化的问题,这里就不展开了。

    b.数据库

    数据库一方面是为了进行指标记录的持久化,另一方面是为了一天之前的数据查询。
    这部分其实真的没什么说的。大家对mysql都是很熟悉的。
    这部分会涉及字段设计、索引设计(尤其是联合索引和覆盖索引)、分库分表(路由规则设计)。虽然一般分库分表前都会有主从同步,但在我们的场景下,还真没太大必要,毕竟写多读少。而大多数读的压力,又放到了缓存上。我们的主从同步,也是为了服务分库分表,提高可用性。

    字段设计

    字段设计方面,一方面尽可能按照范式拆分。这不是电商场景,需要尽可能大宽表,这两个场景完全不同。比如,记录里面可能是id、创建时间、公司、场景、目标、指标、指标值。但完全可以id、创建时间、指标id、指标值,再加一张指标绑定表。这样可以节省非常多存储,也可以大幅提高查询性能(因为一个数据页可以容纳更多记录,从而降低整体IO成本)。
    另外,真的需要需要在记录中保存类似公司这样的字段,最好转化为公司编码,进行保存。

    c.统计表

    我们在数据查询时,常会查看过去一个月、过去一年等数据,进而观察数据趋势。
    而这个数据是不可能在每次查看时,从数据库中拉取的。那服务器会崩溃的。
    所以,会在每天凌晨构建天、周、月、年这样的统计表,甚至可以结合其他维度各搜索条件,生成多个搜索结果。
    具体实现,有三种途径:

    1. 应用程序拉取备库数据,进行统计,并将结果写入统计表中。
    2. 备用数据库通过计划任务,定时触发执行统计,并将结果写入统计表。
    3. 利用大数据技术,如ODPS等MapReduce技术,定时拉取&统计数据,将结果写入统计表。

    d.归档表

    超出一年多数据,查询量很低。即使有查询,往往也是类似平均值这样的聚合数据统计。但随着时间的推进,超出一年的数据,往往会越来越多。所以有的地方就直接拒绝这样数据的详细查询。而另外一种方案,就是将其放在一个归档数据库,不必性能很好,只要可以查询即可。
    当时我们的方案,就是将数据以一年为单位放在一个新的数据库中。即每到新的一年,则将往年的数据写入到一个新的数据库中,作为归档表。而实时数据库则最多只保留最新十三个月的数据。

    当然,归档表的数据也可以放在HBase这样的Bigtable中,尤其在达到一定体量后。其rowKey的kv获取,以及rowKey的scan获取都符合归档表的需求。至于HBase的全表扫描,就算了。。。

    五、数据查询

    其实数据存储部分,已经提到了很多数据查询的思路。
    这里按照两个查询维度的视角,进行分析。

    1.详细数据查询

    详细数据查询,则是直接查询数据记录,而不是统计数据。

    a.短期数据

    短期数据,如一天内的数据,可以直接从缓存中获取。
    当然也可以按照实际情况,将部分类型数据的失效时间,调整一下。

    b.中期数据

    中期数据,如一年内的数据,可以直接从数据库中获取。
    同样,中期的数据,可以按照实际情况,调整为半年等。

    c.长期数据

    长期数据,如一年外的数据,可以从归档数据库中获取(实现基础,可以是独立的冷数据Mysql实例)。

    d.小结

    详细数据查询,必须确保各个range范围的数据,都可以得到有效处理。
    如果一个数据查询范围为最近半年到最近一年半的数据,怎么办呢?一方面可以直接从归档数据库查询。另一方面可以进行查询范围的分解,如将上述范围分解为最近半年到最近一年(数据库),以及最近一年到最近一年半(归档数据库)。前者实现简单,后者用户体验会更好一些,具体需要根据业务需求来进行确定。

    2.统计数据查询

    这里,我给出当时我们多个方案,以及优缺点。

    a.方案一

    方案:应用服务器直接拉取目标范围的数据,然后对其中的数据,进行采样&平均。
    例子:目标范围10W数据,就应用服务器直接拉取10W数据,然后采样其中1W数据,然后每十个数据平均一下,最终得到1k数据,交给前端。
    优点:实现简单
    缺点:随着目标数量的上升,查询效率线性下降。也就是目标数量上升一个数量级,查询时间就上升一个数量级。
    PS:在日常生活&工作中,不可避免负面影响增长,需要杜绝指数、避免线性、追求对数。尤其技术中,很多都可以将线性转为对数。比如流程抽象等。

    b.方案二

    方案:应用服务器,获取开始时间的数据min_id,以及结束时间的数据max_id,进而获得count = max_id - min_id,以及步长pace = count / 100(100表示返回给前端的数据条数)。通过min_id和pace,计算目标数据的id集,进而通过mysql查询结果集。
    例子:根据目标范围的开始时间,查询到min_id = 2000000,max_id = 3000000。进而获得count = 1000000,以及pace = 10000。所以目标id集为:2000000,2010000,2020000,2030000 ... ... 2990000。进而获得mysql中对应结果集。
    优点:实现并不复杂,只涉及代码编写。并且不会随着数据量的增长,而查询性能下降。
    缺点:无法添加时间以外的查询条件;无法处理非连续采集的数据(比如七天的时间范围,有六天是不采集的。但这样的方案,是无法满足条件的)

    c.方案三

    方案:方案三是在方案二的基础上,添加了对其他条件的过滤(有些类似mysql二级索引结果,回表验证其他条件)。
    例子:根据目标范围的开始时间,查询到min_id = 2000000,max_id = 3000000。进而获得count = 1000000,以及pace = 10000。所以目标id集为:2000000,2010000,2020000,2030000 ... ... 2990000。进而获得mysql中对应结果集。然后再针对结果集,进行其他条件的过滤
    优点:实现并不复杂,只涉及代码编写。并且不会随着数据量的增长,而查询性能下降。可以添加时间以外的查询条件
    缺点:查询结果数量无法确定,存在返回结果数为0的可能(这个可以二次查找,调整min_id,拼概率);无法处理非连续采集的数据(比如七天的时间范围,有六天是不采集的。但这样的方案,是无法满足条件的)

    d.方案四

    方案:方案四是在方案二的基础上,将id改为了时间范围。
    根据目标范围的开始时间min_time与结束时间max_time,获得时间差time_range = max_time - min_time,进而获得时间步长time_pace = time_range/100(100表示返回给前端的数据条数)。通过min_time和time_pace,获得目标时间范围集:min_time ~ min_time + time_pace、min_time + time_pace ~ min_time + time_pace * 2 ...
    例子:略
    优点:实现并不复杂,只涉及代码编写。并且不会随着数据量的增长,而查询性能下降。
    缺点:无法添加时间以外的查询条件;性能较低

    e.方案五

    方案:建立统计表,每天凌晨,都会计算前一天各指标数据的,多个搜索条件下的平均值等特征值。便于后续进行“过去一个月”、“过去一年”等时间范围的数据查询。
    优点:针对常见查询,可以快速返回结果。
    缺点:部分查询条件无法进行统计,导致无法查询。统计表的计算,是存在较大资源损耗的。并且由于统计表的时长特性,可能导致展现层数据点数量不统一(有的时候数据点很多,有的时候数据点非常稀疏。但可以通过自动统数据维度升降击毙,进行较大的优化)。

    六、总结

    总结一下,数据架构的核心就是冷热隔离、分级处理。
    在设计数据架构前,需要确认数据情况,如物联网场景写多读少,并且写入数据都是Insert,不存在一致性问题。那么在设计数据解决方案时,侧重点则会有所倾向。

    其实该方案和业务、应用架构等场景下都是一致的。大家都听过什么管理杠杆率、二八定律、好钢要使在刀刃上这些语句。其实总结起来就一句话:
    按照投入产出比,进行资源分配,从而在有限的资源下,追求获得最高整体产出。

  • 相关阅读:
    通过盘古分词自定义规则功能实现软件版本号的提取
    Js event事件在IE、FF兼容性问题
    Android动画效果 translate、scale、alpha、rotate 切换Activity动画 控件位置调整
    iPhone代码片段收集(持续更新)
    Activity之间的相互调用与传递参数
    android如何拍照以及返回拍的图片(经过验证的实际例子)
    Android API :SMS短信服务处理和获取联系人
    实现Android的消息通知栏
    iPhone开发 调用摄像头进行拍照等操作
    Android模拟 HTTP multipart/formdata 请求协议信息实现图片上传
  • 原文地址:https://www.cnblogs.com/Tiancheng-Duan/p/15088304.html
Copyright © 2011-2022 走看看