时序数据库技术体系 – InfluxDB TSM存储引擎之数据读取
http://hbasefly.com/2018/05/02/timeseries-database-7/
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的执行流程进行了形象化说明。整个读取的示意图附件: