1.前言
随着水电控制理论与技术的不断发展,水电装备日趋精密,大型超大型机组不断涌现,使得水电站监控系统数据量不断膨胀,再加上状态检修分析与决策功能的需要,水电站监控系统逐渐出现历史数据存储海量化的趋势。因此在现有软硬件技术水平下提高水电站监控系统历史数据存储与查询效率,特别是模拟量历史数据存储与查询效率,对于进一步提高水电站监控系统稳定性,支撑高级分析与决策功能意义重大。
2.常规数据库设计
由于上世纪80-90年代的广泛使用,现有的水电站监控系统大多采用关系型数据库实现历史数据存储功能。关系型数据库是建立在关系模型基础上的数据库,借助于集合代数等数学概念和方法来处理数据库中的数据。关系模型由关系数据结构、关系操作集合、关系完整性约束三部分组成。
应用关系型数据库的难点在于表设计,这里为了突出主要问题,忽略索引设计等细节。以水电站监控系统模拟量数据存储为例,最简单的表设计如下,
时间(PK) | 点号(PK) | 测值 | 品质 | … |
其中时间和点号为联合主键, 测值品质等属性非空。关系型数据库查找数据主要依靠索引,当单表的记录条目数超过100万条之后,其查询与写入效率将大幅度下降。 为了提高数据库使用效率,主要采用如下方法进行优化:
1)采用分区表技术
由于数据库的查询效率是基于单位物理存储的,因此关系型数据库中提供了分区表功能,将多个不同的物理存储单元组合成一个逻辑上的表,从而提高表的查询与写入性能。你可以理解成由n张不同的数据库小表,组合成了一个大表,只要维持小表中的记录数在100万条以下就可以保持数据库系统的性能。
分区可以很好的简化sql操作,但分区会占用更多的系统资源,存在分区的数量上限。比如mysql数据库下,访问分区表会同时打开该表下的所有分区文件,由于操作系统限制,可能会达到同时打开文件的数量限制;在oracle数据库环境下,分区数量也有上限。同时分区方式减少单个分区内的数据量,对划分分区的策略也有技术方面的要求。因此针对同一个业务的不同项目,分区策略可能也需要相应的改动。一般来说,分区表的创建通过项目初始化时通过脚本进行创建,对调试人员的技术能力提出要求。
2)采用分表策略
当分区的数量接近瓶颈仍然不能满足业务需求或调试人员的技术能力有所限制时,就必须对数据进行分表,分表一般根据业务的测点规模和时间进行划分,比如按100个测点按月进行分表。分表策略从存储的角度上说,可以方便业务系统的部署,降低对调试人员的技术能力要求,但同时会增加查询业务的复杂性,比如按月分表,那么跨月的数据查询就需要通过多条sql或者联合查询才能获得所需的结果。
3)优化行列策略
除了上述的优化策略以外,还可以通过减少数据记录条目数的方式优化。比如同样是一个小时的数据量,如果按照常规的一个时间、一个测点一条记录,单个测点可能需要3600条记录存储;但如果对一个小时的数据进行整合,比如简单的连续排列或者压缩处理,那么就可以将3600条记录压缩成1条记录,减少了记录的条目数就可以达到优化查询的目的。不过由于数据记录进行了整合,插入数据就需要先将数据插入临时表并在固定间隔时间后进行数据处理将数据写入最终的存储结构,这个操作增加了业务复杂度和风险点,比如可能出现单位时间无法处理完所有临时表的情况,对维护工作造成影响;同时后期查询时,即使只是查询单独某个时间点的数据也必须将处理单位时间的完整数据全部拿出,对查询效率造成影响。
总结起来,利用关系型数据库想要达到较好的历史数据存储和查询功能,必须同时考虑查询与存储效率的矛盾、维护成本与编程实现的复杂度之间的矛盾。在小数据量的环境下看,设计思路没有什么困难,但在海量数据环境下这里提到的每一个矛盾都难以解决,需要大量DBA的技术积累,对程序员和普通的维护和调试人员来说不太容易实现。
3.时间序列数据库
关系型数据库的原理决定了其存储数据间关系更高效,但存储时序数据的能力相对较弱,库表的行列设计更复杂。由于水电厂监控系统中需要存储的大部分是时序数据,因此我们考虑有没有更适合时序数据存储的数据库。时序数据是指时间序列数据,即一串按时间维度索引的数据。随着近年来nosql数据库技术的不断发展,出现了专门用于时间序列数据存储的数据库,简称为时间序列数据库。时间序列数据库解决方案通过使用特殊的存储方式,使得时序数据可以高效存储和快速处理,极大提高了时间相关数据的处理能力,相对于关系型数据库它的存储空间减半,查询速度极大的提高。时间序列函数优越的查询性能远超过关系型数据库。
常见的时序数据库包括:Graphite、Influxdata、 Prometheus、OpenTSDB、RRDtool、Druid。
指标 | Influxdata | RRDtool | Graphite | OpenTSDB |
技术栈 | Go | C | Python | Java |
存储方案 | KV | RRD | Whisper | HBase |
SQL-like | 支持 | 不支持 | 不支持 | 不支持 |
引擎速度(时序速度) | #1 | #4 | #3 | #6 |
|
|
|
|
|
其中从时序数据读写的性能上说Influxdata表现最好,且提供接口的编程语言扩展性最多。采用Influxdata与mysql数据库在相同环境下进行压力写入测试,结果如下,
测点数量 | 1w | 5w | 10w |
Influxdata耗时(毫秒) | 531 | 1578 | 3250 |
Mysql耗时(毫秒) | 540 | 2371 | 6981 |
从测试结果来看,时序数据库对关系型数据库的在处理时序数据的情况下性能优势较为明显,并且随着记录条目数的增加,关系型数据库的处理速度会不断降低,而时序数据库在相同数据压力下的性能不会随时间变化而改变。综上所述,采用Influxdata数据库搭建水电厂监控系统历史数据服务。
4.基于influxdata的历史数据服务
采用Influxdata实时顺序数据库搭建历史数据服务,与关系型数据库相比,库表结构得到很大简化,比如前面提到的水电站监控系统模拟量数据,可以直接采用最简单的字段设计,如下表,
时间 | 点号 | 测值 | 品质 | … |
由于数据库表设计不需要考虑分表、分区策略,查询与写入功能的编码工作大大简化;同时也不需要建库脚本,降低了部署难度,对于维护和调试人员的技术要求明显降低。
在简化库表结构的同时,历史数据服务功能的整体结构也得到了简化,如下图:
其中,数据抽取程序负责定时从实时数据库中获取数据,缓存后写入Influxdata数据库;数据查询程序负责接收客户端界面的查询请求,从Influxdata中查询数据,并最终向界面返回查询结果。可以看出,服务的整体结构非常简单。不需要考虑复杂的行列优化策略,因此不需要增加二次数据处理策略,不引入新的风险点。
综上所述,采用Influxdata实时顺序数据库搭建水电厂监控系统历史数据服务,可以在简化编码复杂度、简化部署难度的情况下,提升水电厂监控系统的历史数据存储和查询效率,且效率不会随系统运行时间的变长而下降。
5.结语
上述历史数据服务已经在青海西宁新能源集控监控系统、向家坝水电站智能预警系统等项目中成功运行,运行效果良好,其全部功能达到设计要求。特别是青海西宁新能源集控监控系统项目中,需要入库存储测点的数据量已经超过300w测点,系统历史数据读写性能仍满足监控系统的常规要求,切实提高了历史数据服务的实时性,对于水电厂监控系统历史数据存储优化提供了一种新的途径。