工业领域基于MongoDB设计时序数据库及应用
首先什么是时序数据库
工业领域大部分都是使用的实时数据库,对于物联网到来的今天,传统的实时数据库已经不能满足现在的需求。他们有很多共同点:
- 都带有时间戳
- 按时间顺序生成的
- 大多为结构化数据
- 采集频率高
- 数据量大
实时数据库一般具备一下特定:
高速写入的能力:工业实时数据库通常会对写入的速度有很高的要求。以流程工业的场景为例,每个环节都会设置传感器,每个传感器的采集频率都很高,所以写入的并发量会特别大,有时甚至会要求每秒上百万的测点。所以除了对软件的要求之外,也会选用一些高性能的服务器。
快速查询的能力:查询的需求分为两块,一是要响应实时的查询请求,用于及时反映系统的状态;二是历史数据也要能快速被查询,由于历史数据的量非常大,在查询时需要对特定时间段的数据做聚合,需要做到即使是查一整年的数据情况,也能很快的反应出来。
超强数据压缩能力:上面提到监控数据会被存储很长时间,5年甚至是10年都是常有的事,在存储容量有限的情况下,就需要对数据做一定的压缩,通常压缩方式会分成无损压缩和有损压缩,相比而言,有损压缩的压缩比会更大一些,有时甚至会达到1:30-40,这就需要设计合理的算法来保留数据中的细节,使数据在还原后仍能保留重要的特征。
积累丰富的工具:传统的实时数据库的解决方案一般是从采集开始到直可视化的一整套系统,有多年积累形成的丰富的工具包,比如会积攒上百种的协议,或者各种场景的数据模型,这些都是工业软件的重要竞争力。
追求稳定:工业上对软件的稳定性要求特别高,除了用主备来保证高可用外,完全由软件的质量来保证程序的持续运行,工程师会自豪地拍胸脯保证软件跑十年也不会出错。
时序数据库特点:
1. 单条数据不会很长,但是数据量很大
2. 它们都带有时间戳,且按顺序生成
3. 数据大部分都是结构化的,用于描述某个参数在某个时间点的特征
4. 写入的频率会比查询的频率高很多
5. 已存储的数据很少有更新的需求
6. 用户会更关心一段时间的数据特征,而不是某一个时间点
7. 数据的查询分析大多基于某一个时间段或者某个数值范围
8. 需要进行统计和可视化的展示
随着IoT和工业互联网带来的新一波风口,一系列新的生产方式、组织方式和商业模式开始涌现。物联网技术逐步渗透工业,不断增长的传感器、飙升的数据量,以及更高的大数据分析需求对实时数据库传统的技术架构提出了挑战。有些问题是需要直面的:
扩展性遇到瓶颈。传统的技术架构虽然能保证单机具备极高的性能,也可以通过增加机器使性能线性扩展,但是不能像分布式系统那样实现动态灵活的扩容和缩容,需要提前进行规划。当业务升级需要系统扩容时,老架构的扩展性就很难满足需求了。
无法和大数据生态对接。数据采集的终目的是被理解和使用,大数据产业中对于海量数据的存储分析已经有很成熟的方案,不论是hadoop还是spark的生态圈,都面临着新老技术的对接。很多工业企业因为想使用新的大数据分析技术,不得不对现有的系统进行升级或是替换。
价格高昂。传统的工业实时数据库解决方案价格都十分昂贵,一般只有大型企业能忍痛接受。但是随着新技术新理念的普及,更多的中小企业也意识到数据的重要性,但考虑到资金投入,会倾向于寻找价格更低廉的方案。
这时候来自互联网大家庭的时序数据库方案就展现出了一些先天优势,比如:
分布式架构的天然优势:传统的实时数据库多是主备的部署架构,通常要求有较高配置的机器,来追求单机的性能;同时,在稳定性方面,会对运行软件的稳定性做极高的要求,完全由高质量的代码来保证运行的稳定;由于存储容量有限,也会要求超高的数据压缩比。但是时序数据库的分布式架构,使得系统能够轻松的进行水平扩展,让数据库不再依赖昂贵的硬件和存储设备,以集群天然的优势来实现高可用,不会出现单点的瓶颈或故障,在普通的x86服务器甚至是虚拟机上都可以运行,大大降低了使用成本。
更灵活的数据模型:传统的实时数据库由于工业场景的特殊性,常使用的是单值模型,一个被监控的参数称为一个测点,在写入时会对每一个测点建一个模型,比如一个风机的温度指标算一个测点,十个风机的十个指标就是100个测点,每个测点会附带描述信息(名称、精度、数据类型、开关量/模拟量等)查询的时候就会针对每个测点去查询数值。单值模型的写入效率会很高。
接下来就是我自己基于MongoDB设计时序数据库架构的思路:(没有一个架构设计能适合所有的应用场景规范。无论哪种架构,都需要权衡利弊)
对每个数据Tag,建立一个Collection 以PK命名 如下图:
数据机构设计:
_id:MongoDB Document pk
Count:Document 数据大小
Data:数据 s:时间戳(差值:可以有效减数据大小)v:数值
StartTimeStamp:起始时间
LastTimeStamp:结束时间
可以看到数据存储在Data里面 随着数据量不断扩大 Data 线性增长 可以对Data数据进行简单优化:
对 s v 数据类型进行设计可有效减少磁盘空间的占用 s 时间戳 利用差值存储 4byte 即可 根据需要存储的数据类型 v float类型 也是 4byte 满足大部分应用场景 这样 每个数据 仅占8byte 磁盘空间 当然还可以进一步优化及压缩 以后再说。
按照数据数量分组分段(Document)
分段数据具有显着的优势。基于时间的分段将整整一段时间的数据存储到单个文档中。在诸如 IoT 的基于时间的应用中,传感器数据可以以不规则的间隔生成,并且一些传感器可以提供比其他传感器数据更多的数据。在这些场景中,基于时间的分段可能不是架构设计的佳方法。另一种策略是基于大小的分组。
通过基于大小的分组,我们根据一定数量的发射传感器事件或一整天(以先到者为准)围绕一个文档设计我们的模式。
要查看基于大小的存储分区,请考虑存储传感器数据并将存储区大小限制为每个文档3600个事件或一天(以先到者为准)的方案。注意:3600限制是任意数字,可以根据需要进行更改,无需更改应用程序或模式迁移。
如下图:
数据库设计完毕,目前场景下可满足数据的存储需求。但 IoT场景下有大量的传感器及信息要存储对于吞吐量有很大的要求。经过测试MongoDB 写入数据速度并不是很快 大致5000条/秒(没仔细测过,不同硬件配置 数值也可能不同),对于工业场景写入数据量也不算很大,这样需要对时序数据库增加缓存队列。可以采用Kafka,这里我们自己设计一套简单的消息队列让其满足万级数据量的队列存储。
缓存队列设计如下:
00010203
04050607PK0000-0000FFFFFFFF
FFFFFFFF
0000-03FF
var0 meta0000-0400head
tailDATA0000-0408data.k
data.v0000-0418data.k
data.vdata.k
data.v0001-03FFdata.k
data.vvar1meta0001-0400head
tailDATA0000-0408data.k
data.vdata.k
data.vdata.k
data.v0002-03FFdata.k
data.vvar2meta0002-0400head
tailDATA0000-0408data.k
data.vdata.k
data.vdata.k
data.v0003-03FFdata.k
data.v
var1023meta03FF-0400head
tailDATA03FF-0408data.k
data.vdata.k
data.vdata.k
data.v03FF-03FFdata.k
data.v
利用MomeryMappedFile既能保证队列存储在内存中,又能防止数据丢失问题产生。打开一个文件,映射到内存一块区域。灰色区域用来存储Tag的PK,来标识数据。每个数都有一定大小(可设置)的存储空间, 其中黄色区域是数据的mate 用来存储队列head(队列个的地址)tail(队列后一个的地址),绿色区域DATA用来存储有效数据 根据之前设计的数据结构data.k 存储时间戳 data.v 存储数值。
队列(FIFO)的基本原理是:
入队时:把入队数据写到tail后面的地址 并更新meta中tail的地址。需要增加一个判断是否到达内存空间的后一个地址如果当前tail是后一个地址,则将数据写到个地址并把meta中tail更新到个地址。如果tail中的地址正好等于head中的地址,则表示数据空间已满,实现FIFO时head需要更新到下一个地址。
出队时:读取当前head地址中的数据,并更新head地址到下一个地址。需要增值一个判断head地址=tail地址 则队列为空,不做任何操作。
以上是内存映射队列的一个基本思路,还需要考虑锁和并发问题。目前用netcore2.1实现 并已经在项目中应用。
这样一个简单的时序数据库,就设计完成了。当然还有很多不足,也没有经过大量的测试。
更多资料请关注 智雨物联 官网 http://www.krmes.com
相关文章