问题背景传感器(DCS)常被放置在发电设备中,用于采集数据以监控设备的运行情况。某集团设计的电力监控统计系统需要实时采集传感器数据。保存,然后按时间段提供实时查询统计。系统设计规模将支持20万个传感器(以下简称测点),采集频率为每秒一个数据,即每秒总共有20万个数据,总时间跨度大于1年。在此基础上,可以实现任意指定时间段内多个测点的数据统计,包括最大值、最小值、平均值、方差、中值等。系统原结构图为:系统中,统计响应延时用户期望的是:从200000个测点中随机抽取100个测点,最高统计频率可以每隔几秒调用一次,从总时间跨度数据中算任意一天,期望执行时间在1分钟以内,并且有会是几个线下任务,最长的时间跨度长达一年。现有数据中心没有计算能力,只存储数据,需要通过RESTful接口检索数据进行计算。经测试,通过RESTful接口从数据中心取数据需要10分钟,取100个测点一天的数据量需要10分钟。计算还没有开始,取数据的时间已经远远超过了预计完成计算的时间。基于现有结构,要完成上述统计任务,性能不能满足预期要求,需要对数据进行重新存储。解决方案第一步整理数据,计算需要的数据结构如下:|字段名称|类型|中文名||---|---|---||标签全名|字符串|测量点名称||时间|整数|时间戳||类型|价值|数据类型||质量|价值|质量代码||价值|浮点数|从20万个测点中随机抽取100个测点的数据,根据每个测点的数值序列计算出最大值、最小值、方差、中值等结果。第二步,确定每天20万个测点数据的存储和计算方案。仅Value字段需要200,000*86400*4字节,内存至少64g。当总时间跨度为1年时,数据量将达到数十T,单台服务器的内存显然放不下。多台服务器集群会带来高昂的管理和采购成本。简单按时间顺序存储的数据可以很快找到对应的时间区间,但即便如此,单个测点每天86400条记录,20万个测点共计17.28亿条记录。每次统计都需要遍历这种规模的数据,也很难满足性能要求。那么以测点数为指标建立指标是否可行呢?索引只能快速定位数据,但如果这些数据不是连续存储在外存中,硬盘有一个最小读取单元,会导致读取大量无用数据,使得计算非常缓慢,也无法以满足性能要求。另外,索引占用的空间会随着数据量的增加而增加,插入数据的维护开销也会更大。如果能够将数据按照测点编号进行物理排序存储,并在测点编号上建立索引,与时间序列物理排序存储相比,在查找时,查找的测点记录变为紧凑,需要读取的块也减少了。不够。100个测点的数据存储在一个小于300m的文本文件中,即使使用外存也能满足性能要求。当只有历史冷数据时,处理比较简单,可以按照指定的字段每隔一段时间对数据进行排序。但实际上每秒钟都会有20万个测点的新数据,因为历史数据的规模是巨大的。甚至重写它的时间成本也是不可接受的。这时候就需要区别对待冷热数据。可以按照测量点的顺序准备不变的冷数据。这里有一个小的解决方法,因为需要删除非常早的数据(例如一年前)。如果所有冷数据都按测点排序,数据维护会比较麻烦。删除早期数据将导致所有数据被重写。因此可以设计成先按时间分段,每个时段内的数据按测点和时间排序,整体数据仍按时间排序。任务需求是按天计算的,所以这里按天分段比较合适,跨度更大的离线计算性能损失也不会太大。每当一天过去,昨天的数据按照上述规则排序存储,当天的数据作为热点数据。但是当天的数据量还是太大了,还是不能完全加载到内存中,需要重新划分。经过一些测试确认,我们发现将数据按热分成三层就足够了。第一层,通过接口将十分钟内的热点数据读入内存;第二层,每隔10分钟,将过去10分钟的内存数据按照测量点和时间顺序存入外存;第三层,每一天,将过去24小时内每10分钟的所有数据,按照测量点和时间排序合并。总数据为:一年的数据包括365条日常数据,加上144条当天数据和一条内存数据。分层后的冷热数据属于不同的数据源,需要对同一源数据的结果进行独立计算,然后合并计算最终的统计结果。即使计算方差和中位数需要全内存统计,一天100个测量点的数据量也只需要64m内存。第三步,确定技术选型和方案。从上述存储方案可知,实时数据需要按时间进行分段,在分段内按照测点编号和时间进行物理有序存储。传统的数据库显然做不到这一点。.另外,拆分数据需要支持按照自定义时间段灵活拆分;数据存储必须有高性能索引;冷热数据属于不同的层(不在同一个数据源),需要分别计算然后合并计算。要完成这个任务,用Java硬编码是一个巨大的工作量,用Spark写也很麻烦。开源的esProcSPL语言对上述所有算法都提供了支持,包括高性能文件、物理有序存储、文件索引等机制,让我们可以用少量代码快速实现这种个性化计算。数据检索不能再使用原有系统的RESTful接口,不适合直接通过API从DCS获取数据。用户同意后,引入Kafka缓冲数据,屏蔽DCS层,将DCS数据提供给不同的消费者。变更后的系统结构图如下:描述:DCS系统每秒向KafkaMQ推送20万个测点数据。KafkaMQtoSPL:使用SPL基于KafkaAPI封装的Kafka功能连接Kafka消费数据。内存缓冲:循环从kafka(kafka_poll)消费数据,每个周期保证10秒以上的数据量,完成每轮前10秒的数据,存为文件,根据测量读入内存点和时间顺序。分层数据文件:按不同时间段对冷热数据文件进行分层。统计时,冷热数据混合计算。支持每个测量点名称对应一个CSV文件作为数据源进行计算。统计接口以HTTP服务的形式被外部应用调用,统计结果通过回调接口返回给外部应用。第四步是实施优化方案。现有的RESTful接口取数据太慢,改接口从Kafka消费数据。存储数据时,将字符串类型的测点名称数字化保存,可以获得更小的存储容量和更好的计算性能。第二步提到,当数据量很大时,不可能把所有的数据都放在内存中进行计算,所以可以考虑采用冷热分层的方案,将数据分成三层。时间是有序的(后面所有的外部存储文件都是按这个顺序存储的,不再赘述),存储在组表中,因为大表对性能影响很大,存储组表有利于提高系统的整体性能;每10分钟的冷数据,set文件存储,因为set文件更容易创建和使用,存储小表非常方便,不会因为索引块而降低存储效率;Kafka10分钟内的热点数据直接读取内存,因为数据本身是通过Kafka接口获取的,数据可能存在一定的延迟,不适合每秒读写一次。测试后发现,如果从Kafka中获取10分钟内的热点数据,然后解析成JSON,不仅内存消耗大,解析JSON的时间也很长。这样就没办法直接加载内存中的热点数据进行统计计算了,所以每隔10秒将热点数据改成set文件保存。接下来开始实现统计计算部分。日组表的冷数据计算很快,但是日144集文件的计算很慢。通过计算可知,每10分钟的数据量约为1.2亿条记录。这种规模的数据可以存储在组表中。另外,可以每2小时增加一层组表文件,减少一天的文件总数。数量(24而不是144)。实际上,计算中使用的二分查找是针对单个文件中有序的测点编号进行的,减少了文件个数,也就是减少了总的查找次数。最后,我们将数据分为4层。第一层:设置文件的热点数据,延时10秒;第二层,组表每10分钟一次的冷数据;第三层,组表每2小时一次的冷数据;第四层,每天组表的冷数据。由于每一层的数据都是按照测点编号和时间排序的,因此可以将每一层合并,快速生成下一层的数据文件。这时候冷数据的计算速度非常快,可以满足实际使用,但是热数据的计算相对于冷数据还是很慢的。观察发现热数据的所有集合文件加起来大概3G左右,不是很大,内存可以放得下。在实际测试中,将文件读入内存并进行查找比直接查找外存文件要快数倍。已知的统计计算分为最大值、最小值、中值、方差、平均值等,不尽相同,但前面的数据搜索是相同的。都是用二分法找出测点编号组对应的数据,然后用时间过滤得到对应的值。实测效果经过几天的SPL编码和测试,优化效果非常明显。优化后的测试结果如下(耗时毫秒):|测点时间段|10|50|100||---|---|---|---||10分钟|467|586|854||1小时|第1739章3885|4545||6小时|2599|7489|13138||1天|4923|16264|30254|注意:测试环境使用的机械硬盘对并发计算不友好,应更换为固态硬盘,安装后测试结果会有很大提升。后记解决性能优化问题,最重要的是设计一个高性能的计算方案,有效降低计算复杂度,最终提高速度。因此,一方面需要充分了解计算和数据的特点,另一方面也需要熟悉常见的高性能算法和存储方案,才能设计出合理的优化方案因地制宜的解决办法。本作品中使用到的基本高性能算法和存储方案可以在以下课程中找到:点此学习性能优化课程,有兴趣的同学可以参考。传统数据库的功能比较单一,只能解决一个环节的问题。比如内存数据库解决热数据问题,大数据平台解决冷数据问题。但是,当前的问题需要多种技术的结合。如果采用多种产品混合实现,会带来架构的复杂性,增加系统的风险。而且业界大型数据库产品的架构比较死板,基本不提供存储层的可编程性,很难基于这些产品实现一些特殊的设计方案。相比之下,集算器具有开放的技术架构和强大的编程能力(SPL语言),可以深度控制,实现因地制宜的各种解决方案。SPL资料SPL下载SPL源码欢迎关注我的公告号:字母哥杂谈,回复003赠作者专栏《docker修炼之道》30余篇优质docker文章PDF版。Antetokounmpo博客:zimug.com
