时间序列数据的存储和计算 - 开源时序数据库解析(四)

本文涉及的产品
对象存储 OSS,20GB 3个月
日志服务 SLS,月写入数据量 50GB 1个月
对象存储 OSS,恶意文件检测 1000次 1年
简介: Prometheus 开源时序数据库解析的系列文章在之前已经完成了几篇,对比分析了Hbase系的OpenTSDB、Cassandra系的KairosDB、BlueFlood及Heroic,最后是tsdb ranking top 1的InfluxDB。

Prometheus

开源时序数据库解析的系列文章在之前已经完成了几篇,对比分析了Hbase系的OpenTSDB、Cassandra系的KairosDB、BlueFlood及Heroic,最后是tsdb ranking top 1的InfluxDB。InfluxDB是从底到上纯自研的一款TSDB,在看他相关资料时对其比较感兴趣的是底层的TSM,一个基于LSM思想针对时序数据场景优化的存储引擎。InfluxDB分享了他们从最初使用LevelDB,到替换成BoltDB,最后到决定自研TSM的整个过程,深刻描述了每个阶段的痛点及过度到下个阶段需要解决的核心问题,以及最终TSM的核心设计思路。这类分享是我比较喜欢的,不是直接一上来告诉你什么技术是最好,而是一步一步告诉你整个技术演进的历程。这其中对每个阶段遇到的问题的深刻剖析、最终做出技术选择的理由等,让人印象深刻,能学到很多东西。
但InfluxDB的TSM,细节描述还是不够多,更多的是策略和行为的描述。最近看到了一篇文章《 Writing a Time Series Database from Scratch》,从零开始写一个时序数据库,虽然有点标题党,但内容确是实打实的干货,描述了一个TSDB存储引擎的设计思路。而且这个存储引擎不是一个概念或玩具,而是真实应用到生产了,是Prometheus在2017年11月对外发布的2.0版里的一个完全重写的新的存储引擎。这个新版存储引擎号称是带来了『huge performance improvements』,由于变化太大,做不到向后兼容,估计也是真的带来了很多惊喜,才能这样子去耍流氓。
而本篇文章,主要是对那篇文章的一个解读,大部分内容来自原文,略有删减。想了解更详细的内容的话,建议可以去看英文原文,有理解错误的地方欢迎指正。

数据模型

Prometheus与其他主流时序数据库一样,在数据模型定义上,也会包含metric name、一个或多个labels(同tags)以及metric value。metric name加一组labels作为唯一标识,来定义time series,也就是时间线。在查询时,支持根据labels条件查找time series,支持简单的条件也支持复杂的条件。存储引擎的设计,会根据时序数据的特点,重点考虑数据存储(写多读少)、数据回收(retention)以及数据查询,Prometheus这里暂时还没提数据分析。
e34a71f5-2b52-4684-877c-00fdc617a7c5.png
上图是所有数据点分布的一个简单视图,横轴是时间,纵轴是时间线,区域内每个点就是数据点。Prometheus每次接收数据,收到的是图中区域内纵向的一条线。这个表述很形象,因为在同一时刻,每条时间线只会产生一个数据点,但同时会有多条时间线产生数据,把这些数据点连在一起,就是一条竖线。这个特征很重要,影响数据写入和压缩的优化策略。

V2存储引擎

这篇文章主要阐述的是新的V3存储引擎的一些设计思想,老的存储引擎就是V2。V2存储引擎会把每条时间线上的数据点分别存储到不同的文件,这种设计策略下,文中提出了几个问题来探讨:
  1. 针对写入要做的优化:针对SSD和HDD的写入优化,均可遵循顺序写和批量写的原则。但是如上面所说,Prometheus一次性接收到的数据是一条竖线,包含很多的数据点,但是这些数据点属于不同的时间线。而当前的设计是一条时间线对应一个独立的文件,所以每次写入都会需要向很多不同的文件写入极少量的数据。针对这个问题,V2存储引擎的优化策略是Chunk写,针对单个时间线的写入必须是批量写,那就需要数据在时间线维度累积一定时间后才能凑到一定量的数据点。Chunk写策略带来的好处除了批量写外,还能优化热数据查询效率以及数据压缩率。V2存储引擎使用了和Facebook Gorilla一样的压缩算法,能够将16个字节的数据点压缩到平均1.37个字节,节省12倍内存和空间。Chunk写就要求数据一定要在服务器内存里积累一定的时间,即热数据基本都在内存中,查询效率很高。
  2. 针对查询要做的优化:时序数据的查询场景多遍,可以查某个时间线的某个时间点、某个时间点多条时间线或者是某个时间范围多条时间线的数据等等。在上面的数据模型图上示意出来,就是在二维象限内一个矩形的数据块。不断是针对SSD还是HDD,对磁盘数据读取比较友好的优化,均是优化到一次查询只需要少量的随机定位加上大块的顺序读取。这个和数据在磁盘的分布有很大的关系,归根到底,还是和数据写有关系,但不一定是实时写优化,也可以通过后续的数据整理来优化。
V2存储引擎里,有一些已经做的比较好的优化策略,主要是Chunk写以及热数据内存缓存,这两个优化延续到了V3。但是除了这两点,V2还是存在很多的缺陷:
  • 文件数会随着时间线的数量同比增长,慢慢会耗尽inode。
  • 即便使用了Chunk写优化,若一次写入涉及的时间线过多,IOPS要求还是会很高。
  • 每个文件不可能会时刻保持open状态,一次查询可能需要重新打开大量文件,增大查询延迟。
  • 数据回收需要从大量文件扫描和重写数据,耗时较长。
  • 数据需要在内存中积累一定时间以Chunk写,V2会采用定时写Checkpoint的机制来尽量保证内存中数据不丢失。但通常记录Checkpoint的时间大于能承受的数据丢失的时间窗口,并且在节点恢复时从checkpoint restore数据的时间也会很长。
另外关于时间线的索引,V2存储引擎使用LevelDB来存储label到时间线的映射。当时间线到一定规模后,查询的效率会变得很低。在一般场景下,时间线的基数都是比较小的,因为应用环境很少变更,运行稳定的话时间线基数也会处于一个稳定的状态。但是若label设置不合理,例如采用一个动态值,比如是程序版本号作为label,每次应用升级label的值都会改变。那随着时间的推进,会存在越来越多无效的时间线(Prometheus称其为Series Churn)。时间线的规模会变得越来越大,影响索引查询效率。

V3存储引擎

V3引擎完全重新设计,来解决V2引擎中存在的这些问题。V3引擎可以看做是一个简单版、针对时序数据场景优化过后的LSM,可以带着LSM的设计思想来理解,先看一下V3引擎中数据的文件目录结构。
65ffda43-77bc-4599-a818-1a69bb66ca66.png
data目录下存放所有的数据,data目录的下一级目录是以'b-'为前缀,顺序自增的ID为后缀的目录,代表Block。每个Block下有chunks、index和meta.json,chunks目录下存放chunk的数据。这个chunk和V2的chunk是一个概念,唯一的不同是一个chunk内会包含很多的时间线,而不再只是一条。index是这个block下对chunk的索引,可以支持根据某个label快速定位到时间线以及数据所在的chunk。meta.json是一个简单的关于block数据和状态的一个描述文件。要理解V3引擎的设计思想,只需要搞明白几个问题:1. chunk文件的存储格式?2. index的存储格式,如何实现快速查找?3. 为何最后一个block没有chunk目录但有一个wal目录?

设计思想

0e32970e-8e07-472c-8899-ae5ef22c15dc.png
Prometheus将数据按时间维度切分为多个block,每个block被认为是独立的一个数据库,覆盖不同的时间范围的数据,完全没有交叉。每个Block下chunk内的数据dump到文件后即不可再修改,只有最近的一个block允许接收新数据。最新的block内数据写入会先写到一个内存的结构,为了保证数据不丢失,会先写一份WAL(write ahead log)。
V3完全借鉴了LSM的设计思想,针对时序数据特征做了一些优化,带来很多好处:
  1. 当查询一个时间范围的数据时,可快速排除无关的block。每个block有独立的index,能够有效解决V2内遇到的『无效时间线 Series Churn』的问题。
  2. 内存数据dump到chunk file,可高效采用大块数据顺序写,对SSD和HDD都很友好。
  3. 和V2一样,最近的数据在内存内,最近的数据也是最热的数据,在内存可支持最高效的查询。
  4. 老数据的回收变得非常简单和高效,只需要删除少量目录。
01c0ca6c-f5ba-4c55-bf70-cda59d0aa80b.png
V3内block以两个小时的跨度来切割,这个时间跨度不能太大,也不能太小。太大的话若内存中要保留两个小时数据,则内存占用会比较大。太小的话会导致太多的block,查询时需要对更多的文件做查询。所以两个小时是一个综合考虑后决定的值,但是当查询大跨度时间范围时,仍不可避免需要跨多个文件,例如查询一周时间跨度需要84个文件。V3也是采用了LSM一样的compaction策略来做查询优化,把小的block合并为大的block,compaction期间也可做其他一些事,例如删除过期数据或重构chunk数据以支持更高效的查询。这篇文章中对V3的compaction描述的比较少,这个倒可以去看看InfluxDB怎么做的,InfluxDB有多种不同的compaction策略,在不同的时刻使用,具体可以看看这篇 文章
67e6f0f3-11ed-46a3-8416-4f74cc4126e6.png
这个图是V3内对过期数据回收的一个示意图,相比V2会简单很多。对整个block已经过期的数据,直接删除文件夹即可。但对只有部分数据过期的block,无法进行回收,只能等全部过期或者compaction。这里有个问题要讨论,随着对历史数据不断的做compaction,block会变得越来越大,覆盖的时间范围会越大,则越难被回收。这里必须控制block的上限,通常是根据一个retention window的周期来配置。
以上基本讲完了数据存储的一些设计要点,还是比较简单明了的。和其他时序数据库一样,除了数据存储库,还有一份索引库。V3的索引结构比较简单,直接引用文章中给的例子:
efc59b7b-cf80-4d23-b5a2-b2c983be2493.png
13447c9d-0a25-4fad-aefc-af839ad0a3e1.png
从文章描述看,V3没有和V2一样采用LevelDB,在已经持久化的Block,Index已经固定下来,不可修改。而对于最新的还在写数据的block,V3则会把所有的索引全部hold在内存,维护一个内存结构,等到这个block被关闭,再持久化到文件。这样做会比较简单一点,内存里维护时间线到ID的映射以及label到ID列表的映射,查询效率会很高。而且Prometheus对Label的基数会有一个假设:『a real-world dataset of ~4.4 million series with about 12 labels each has less than 5,000 unique labels』,这个全部保存在内存也是一个很小的量级,完全没有问题。InfluxDB采用的是类似的策略,而其他一些TSDB则直接使用ElasticSearch作为索引引擎。

总结

针对时序数据这种写多读少的场景,类LSM的存储引擎还是有不少优势的。有些TSDB直接基于开源的LSM引擎分布式数据库例如Hbase或Cassandra,也有自己基于LevelDB/RocksDB研发,或者再像InfluxDB和Prometheus一样纯自研,因为时序数据这一特定场景还是可以做更多的优化,例如索引、compaction策略等。Prometheus V3引擎的设计思想和InfluxDB真的很像,优化思路高度一致,后续在有新的需求的出现后,会有更多变化。
目录
相关文章
|
5天前
|
存储 弹性计算 缓存
阿里云服务器ECS通用型实例规格族特点、适用场景、指标数据解析
阿里云服务器ECS提供了多种通用型实例规格族,每种规格族都针对不同的计算需求、存储性能、网络吞吐量和安全特性进行了优化。以下是对存储增强通用型实例规格族g8ise、通用型实例规格族g8a、通用型实例规格族g8y、存储增强通用型实例规格族g7se、通用型实例规格族g7等所有通用型实例规格族的详细解析,包括它们的核心特点、适用场景、实例规格及具体指标数据,以供参考。
阿里云服务器ECS通用型实例规格族特点、适用场景、指标数据解析
|
25天前
|
数据采集 存储 JavaScript
如何使用Cheerio与jsdom解析复杂的HTML结构进行数据提取
在现代网页开发中,复杂的HTML结构给爬虫技术带来挑战。传统的解析库难以应对,而Cheerio和jsdom在Node.js环境下提供了强大工具。本文探讨如何在复杂HTML结构中精确提取数据,结合代理IP、cookie、user-agent设置及多线程技术,提升数据采集的效率和准确性。通过具体示例代码,展示如何使用Cheerio和jsdom解析HTML,并进行数据归类和统计。这种方法适用于处理大量分类数据的爬虫任务,帮助开发者轻松实现高效的数据提取。
如何使用Cheerio与jsdom解析复杂的HTML结构进行数据提取
|
11天前
|
存储 关系型数据库 MySQL
技术解析:MySQL中取最新一条重复数据的方法
以上提供的两种方法都可以有效地从MySQL数据库中提取每个类别最新的重复数据。选择哪种方法取决于具体的使用场景和MySQL版本。子查询加分组的方法兼容性更好,适用于所有版本的MySQL;而窗口函数方法代码更简洁,执行效率可能更高,但需要MySQL 8.0及以上版本。在实际应用中,应根据数据量大小、查询性能需求以及MySQL版本等因素综合考虑,选择最合适的实现方案。
57 6
|
18天前
|
KVM 虚拟化
计算虚拟化之CPU——qemu解析
【9月更文挑战10天】本文介绍了QEMU命令行参数的解析过程及其在KVM虚拟化中的应用。展示了QEMU通过多个`qemu_add_opts`函数调用处理不同类型设备和配置选项的方式,并附上了OpenStack生成的一个复杂KVM参数实例。
|
26天前
|
XML JSON API
淘宝京东商品详情数据解析,API接口系列
淘宝商品详情数据包括多个方面,如商品标题、价格、图片、描述、属性、SKU(库存量单位)库存、视频等。这些数据对于买家了解商品详情以及卖家管理商品都至关重要。
数据解析之xpath 太6了
数据解析之xpath 太6了
|
15天前
|
SQL 关系型数据库 MySQL
MySQL技术安装配置、数据库与表的设计、数据操作解析
MySQL,作为最流行的关系型数据库管理系统之一,在WEB应用领域中占据着举足轻重的地位。本文将从MySQL的基本概念、安装配置、数据库与表的设计、数据操作解析,并通过具体的代码示例展示如何在实际项目中应用MySQL。
52 0
|
20天前
|
存储 JSON API
Python编程:解析HTTP请求返回的JSON数据
使用Python处理HTTP请求和解析JSON数据既直接又高效。`requests`库的简洁性和强大功能使得发送请求、接收和解析响应变得异常简单。以上步骤和示例提供了一个基础的框架,可以根据你的具体需求进行调整和扩展。通过合适的异常处理,你的代码将更加健壮和可靠,为用户提供更加流畅的体验。
54 0
|
28天前
|
监控 安全 网络安全
|
28天前
|
存储 C# 关系型数据库
“云端融合:WPF应用无缝对接Azure与AWS——从Blob存储到RDS数据库,全面解析跨平台云服务集成的最佳实践”
【8月更文挑战第31天】本文探讨了如何将Windows Presentation Foundation(WPF)应用与Microsoft Azure和Amazon Web Services(AWS)两大主流云平台无缝集成。通过具体示例代码展示了如何利用Azure Blob Storage存储非结构化数据、Azure Cosmos DB进行分布式数据库操作;同时介绍了如何借助Amazon S3实现大规模数据存储及通过Amazon RDS简化数据库管理。这不仅提升了WPF应用的可扩展性和可用性,还降低了基础设施成本。
54 0

推荐镜像

更多