select_stmt = "SELECT" fields from_clause [ into_clause ] [ where_clause ] [ group_by_clause ] [ order_by_clause ] [ limit_clause ] [ offset_clause ] [ slimit_clause ] [ soffset_clause ] .
上半部分:InfluxDB数据读取流程原理
InfluxDB读取流程框架

2. QueryParser:InfluxQL进入系统之后,系统首先会对InfluxQL执行切词并解析为抽象语法树(AST),抽象树中标示出了数据源、查询条件、查询列以及聚合函数等等,分别对应上图中Source、Condition以及Aggration。InfluxQL没有使用通用的第三方AST解析库,自己实现了一套解析库,对细节感兴趣的可以参考:https://github.com/influxdata/influxql。接着InfluxDB会将抽象树转化为一个Query实体对象,供后续查询中使用。
3. BuildIterators:InfluxQL语句转换为Query实体对象之后,就进入读取流程中最重要最核心的一个环节 – 构建Iterator体系。构建Iterator体系是一个非常复杂的逻辑过程,其中细节非常繁复,笔者尽可能化繁为简,将其中的主线抽出来。为了方便理解,笔者将Iterator体系分为三个子体系:顶层Iterator子体系、中间层Iterator子体系以及底层Iterator子体系。
纵观整个Iterator体系的构建,整体逻辑还是很清晰的。总结起来就是,查询按照查询列构建最顶层FieldIterator,每个FieldIterator会根据TimeRange雇佣多个ShardIterator去处理单个Shard上面对应列值的查找,对查找到的值要么直接返回要么执行Reduce函数进行聚合操作。每个Shard内部首先会根据查询条件利用倒排索引定位到所有满足条件的series,再为每个series构建一个TagsetIterator用来查找具体的列值数据。因此,TagsetIterator是整个体系中唯一干活的Iterator,所有其他上层Iterator都是逻辑Iterator。
4. Emitter.Emit:Iterator体系构建完成之后就完成了查询聚合前的准备工作,接下来就开始干活了。干活逻辑简单来讲是遍历所有FieldIterator,对每个FieldIterator执行一次Next函数,就会返回每个查询列的结果值,组装到一起就是一行数据。FieldIterator执行Next()函数会传递到最底层的TagsetIterator,TagsetIterator执行Next函数实际返回真实的时序数据。
TSDB存储引擎执行逻辑
TSDB存储引擎(实际上就是一个Shard)根据用户的查询请求执行原始数据的查询就是上文中提到的底层Iterator子体系的构建。查询过程分为两个部分:倒排索引查询过滤以及TSM数据层查询,前者通过Query中的where条件结合倒排索引过滤掉不满足条件的SeriesKey;后者根据留下的SeriesKey以及where条件中时间段信息(TimeRange)在TSMFile中以及内存中查出最终满足条件的数值列。TSDB存储引擎会将查询到的所有满足条件的原始数值列返回给上层,上层根据聚合函数对原始数据进行聚合并将聚合结果返回给用户。整个过程如下图所示:
下半部分:InfluxDB查询流程示例
timestamp
|
publisher
|
advertiser
|
gender
|
impression
|
click
|
revenue
|
2017-11-01T00:00:00
|
ultrarimfast.com
|
baidu.com
|
male
|
1800
|
23
|
11.24
|
2017-12-01T00:00:00
|
bieberfever.com
|
google.com
|
male
|
2074
|
72
|
31.22
|
2018-01-04T00:00:00
|
ultrarimfast.com
|
baidu.com
|
false
|
1079
|
54
|
9.72
|
2018-01-08T00:00:01
|
ultrarimfast.com
|
google.com
|
male
|
1912
|
11
|
3.74
|
2018-01-21T00:00:01
|
bieberfever.com
|
baidu.com
|
male
|
897
|
17
|
5.48
|
2018-01-26T00:00:01
|
ultrarimfast.com
|
baidu.com
|
male
|
1120
|
73
|
6.48
|
select sum(click),sum(impression),sum(revenue) from table group by publisher where advertiser = "baidu.com" and timestamp > "2018-01-01" and timestamp < "2018-02-01"
步骤一:倒排索引过滤+groupby分组
原始查询语句:select …. from ad_datasource where advertiser = “baidu.com” …… 。倒排索引即根据条件advertiser=”baidu.com”在所有Index File中遍历查询包含该tag的所有SeriesKey,具体原理(详见《时序数据库技术体系 – InfluxDB 多维查询之倒排索引》)如下:
publisher
|
advertiser
|
gender
|
ultrarimfast.com
|
baidu.com
|
male
|
ultrarimfast.com
|
baidu.com
|
false
|
bieberfever.com
|
baidu.com
|
male
|
publisher
|
advertiser
|
gender
|
bieberfever.com
|
baidu.com
|
male
|
publisher
|
advertiser
|
gender
|
ultrarimfast.com
|
baidu.com
|
male
|
ultrarimfast.com
|
baidu.com
|
female
|
步骤二:TSM文件数据检索

在TSM中查询满足TimeRange条件的SeriesKey对应的待查询列值,因为InfluxDB会根据不同的查询列设置独立的FieldIterator,因此查询列有多少就有多少个FieldIterator,如下所示:
步骤三:原始数据聚合
publisher
|
sum(impression)
|
sum(click)
|
sum(revenue)
|
bieberfever.com
|
897
|
17
|
5.48
|
ultrarimfast.com
|
1079 + 1120
|
54 + 73
|
9.72 + 6.48
|
文章总结
本文主要结合InfluxDB源码对查询聚合请求在服务器端的处理框架进行了系统理论介绍,同时深入介绍了InfluxDB Shard Engine是如何利用倒排索引、时序数据存储文件(TSMFile)处理用户的查询请求。最后,举了一个示例对Shard Engine的执行流程进行了形象化说明。整个读取的示意图附件:
范总后续有对influxdb进行分布式改造的方案文章吗?
目前还没有相关的打算 可以私下沟通
您好,我是开发者头条的运营。您的《时序数据库技术体系:InfluxDB TSM 存储引擎之数据读取》已被我们平台用户推荐到首页。感谢您的辛苦创作,为了让更多读者认识您,我们邀请您来开发者头条分享。与创作不同,您仅需复制粘贴文章链接即可完成分享。您可以在各大应用市场搜索 “开发者头条” 找到我们的应用,欢迎了解。期待您的分享。
好的 谢谢
一个博客能让人流连忘返,讲真,靠的是实力!
多谢光顾 哈哈哈
通过楼主文章再重读源码中query流程理解更加深刻了,感谢
拜读了,多多学习总是好的!
你好,我们一直在用influxdb做监控数据存储,但是随着数据量的增大,内存消耗特别大。
想请教一下,内存消耗有计算方法或优化方法吗
我们目前还没有发现这个问题 可能还是要先分析为什么内存消耗严重 消耗在哪里
influxdb可以控制最大使用内存吗
目前没有相关参数
真大神,目前在进行分布式改造方案,受益匪浅
有提高influxdb查询并发性能的方案吗
目前只能从硬件层面做文章 或者改代码
请教一个问题,如果 SQL 为:select sum(click),sum(impression),sum(revenue) from table (这个SQL 没有任何条件)。这样的话:
1,是不是查询时就不经过倒排索引了?
2,在查询 tsm 时,就要使用 series key。如果不经过倒排索引的话,查询 tsm 的 series key 是如何得到的呢?
这块不太记得了 应该是不走倒排索引 但会拿到所有serieskey 再去遍历这些serieskey
有个seriesfile专门存储的所有serieskey
感谢回答,我再去看看。
TDengine 性能更好,集群也开源了
范神,我重读这几篇,还是有个疑问需要请教范神解答:
原文有:
“上图中中间部分为索引层,TSM在启动之后就会将TSM文件的索引部分加载到内存,数据部分因为太大并不会直接加载到内存。用户查询可以分为三步:
1. 首先根据Key(SeriesKey+fieldKey)找到对应的SeriesIndex Block,因为Key是有序的,所以可以使用二分查找来具体实现”
我没弄懂的是,在之前关于 TSM 的文章里到了 Series Index Section 实现了文件级别的 B+Tree,但是这里对 Series Index Block 的查询又是二分查找,请问 B+Tree 结构在其中是什么作用呢?
B+Tree结构应该是整个section的结构,包含三层节点,一层是SeriesIndexBlock这一层,下一层是[MinTime,MaxTime]这一层,叶子是SeriesDataBlock。