时序数据库 Apache-IoTDB 源码解析之元数据索引块(六)

上一章聊到 TsFile 索引块的详细介绍,以及一个查询所经过的步骤。详情请见:

时序数据库 Apache-IoTDB 源码解析之文件索引块(五)

打一波广告,欢迎大家访问 IoTDB 仓库,求一波 Star 。欢迎关注头条号:列炮缓开局,欢迎关注OSCHINA博客

阿里云、东方国信等各家公司正在招聘IoTDB数据库开发工程师,欢迎加我微信内推:liutaohua001

这一章主要想聊聊:

  1. 原有索引中的不足
  2. 新版本中索引的设计

原有索引中的不足

现在来张图回顾一下原有的数据存储方式,在文件尾部使用DeviceMetaDataIndexMap和MeasurementSchemaMap中记录所有设备数据偏移量、传感器的相关信息等。因为使用的是Map结构访问都是O(1)的,但是需要关注的一个问题就是它是在内存中O(1)的,在磁盘上并不能找到什么好的查询方式,唯一能做的就是全部读取出来然后放到内存中。

通常情况下这不会有什么问题,但是使用在工业场景中,传感器+设备很有可能数以百万计,这会引发无论你读取的是一个传感器或者是一个设备的数据,在DeviceMetaDataIndexMap这一段数据都需要完整的从磁盘上读取回来,这好吗?这不好,还拿之前的数据举例:

时间戳人名体温...年龄
1580950800 张一 36.5 ... 30
1580950800 张二 36.9 ... 30
1580950800 张三 36.7 ... 30
1580950800 张.. 36.7 ... 30
1580950800 张两百万 36.7 ... 30

当执行一个查询select 体温 from 张三 where time = 1580950800 时,张一、张二、张三...张两百万这两百万个名字都需要从硬盘中读取并反序列化出来(通常 IoTDB 中的路径是root.T000100010002.A_JB01.JB01.JTJBGMCA6K2052561),也就是1999999个都是多余的读取。

此外,在TsDeviceMetaDataList中,也是按照chunkGroup存储,意味着,如果我仅查询一列,同样会把其它的列信息读取出来。

新版本中索引的设计

为了尽可能贴近只读投影列的思想,新版本中对于TsDeviceMetaDataList部分不再按照设备级别存储,改为按照传感器级别存储,TsFileMetaData部分不再使用map存储所有设备,改为使用多叉树并有序排列。

改动1: 使用将一个传感器的所有ChunkMetaData的存储在一起,并使用TimeseriesMetadata结构进行维护(保存某个传感器的开始的offset及数据长度),如图: 

改动2: 在完成改动1之后,我们得到了TimeSeriesMetadata-年龄,TimeSeriesMetadata-体温, ..., TimeSeriesMetadata-x,在刷盘之前将所有TimeSeriesMetadata按照传感器名字排序,那么在这里就可以进行二分查找了,如图:

改动3: 在一个复杂的设备上传感器很有可能多达数千个,例如:新能源汽车当中,每台车量可以多达4000多个信号项。假如我们需要管理10万辆车,那么依然会有10万 * 4000 = 4亿个TimeSeriesMetadata。还是太多了,假如我们进行多个传感器的值查询时,每次都进行二分,这无疑是浪费的。

所以进行一个树状的提取,假设每1024个提取一次,那么4亿个可以节省为390,625个,假如再抽取一次,那就可以减少到382个了,这样我们从硬盘上一次读取1024个,最多读取3次硬盘就可以完成查找。大意如图: 

改动4: 除了传感器之外,还要考虑的是设备级别,因为设备也是数以万计,但同上面的逻辑是一样的。再此就不进行赘述,如图: 

在代码中使用MetadataIndexNode类进行存储图中的数据,代表了当前节点属于管理设备的节点还是管理传感器的节点,是中间节点还是叶子节点。使用MetadataIndexEntry用来存储具体的信息,也就是TimeSeriesMetadata结构在硬盘上的偏移量或者子节点MetadataIndexNode在硬盘上的偏移量.

附上一个文件展示:


            POSITION|	CONTENT
            -------- 	-------
                   0|	[magic head] TsFile
                   6|	[version number] 000002
|||||||||||||||||||||	[Chunk Group] of root.sg1.d1, num of Chunks:10000
                  12|	[Chunk] of s8289, numOfPoints:500, time range:[0,499], tsDataType:INT64, 
                     	startTime: 0 endTime: 499 count: 500 [minValue:1,maxValue:1,firstValue:1,lastValue:1,sumValue:500.0]
                    |		1 pages
                    		 ...
              350026|	[Chunk] of s0, numOfPoints:500, time range:[0,499], tsDataType:INT64, 
                     	startTime: 0 endTime: 499 count: 500 [minValue:1,maxValue:1,firstValue:1,lastValue:1,sumValue:500.0]
                    |		1 pages
                    		 ...
             1418902|	[Chunk Group Footer]
                    |		[marker] 0
                    |		[deviceID] root.sg1.d1
                    |		[dataSize] 1418890
                    |		[num of chunks] 10000
|||||||||||||||||||||	[Chunk Group] of root.sg1.d1 ends
             1418947|	[marker] 2
             1418948|	[ChunkMetadataList] of root.sg1.d1.s0, tsDataType:INT64
                    |		 [ChunkMetaData] of s0, offset:350026
                    		 ...
             2247838|	[TimeseriesMetadata] name:s0, offset:1418948, chunkMetaDataListDataSize:80
             3116728|	[MetadataIndexNode] nodeType:LEAF_MEASUREMENT, childSize:10
                    |		[MetadataIndexEntry] name:s0, offset:2247838, endOffset:2336808
                    |		[MetadataIndexEntry] name:s192, offset:2336808, endOffset:2425782
                    |		[MetadataIndexEntry] name:s2841, offset:2425782, endOffset:2514757
                    |		[MetadataIndexEntry] name:s3763, offset:2514757, endOffset:2603732
                    |		[MetadataIndexEntry] name:s4685, offset:2603732, endOffset:2692705
                    |		[MetadataIndexEntry] name:s5606, offset:2692705, endOffset:2781680
                    |		[MetadataIndexEntry] name:s6528, offset:2781680, endOffset:2870655
                    |		[MetadataIndexEntry] name:s745, offset:2870655, endOffset:2959629
                    |		[MetadataIndexEntry] name:s8371, offset:2959629, endOffset:3048604
                    |		[MetadataIndexEntry] name:s9293, offset:3048604, endOffset:3116728
             3116906|	[TsFileMetadata] deviceNums:1,chunkNums:10000,invalidChunkNums:0
                    |		[MetadataIndexNode] nodeType:INTERNAL_MEASUREMENT,childSize:1
                    |			[MetadataIndexEntry] name:root.sg1.d1,offset:3116728,endOffset:3116906
                    |		[bloom filter bit vector byte array length] 7794
                    |		[bloom filter bit vector byte array] 
                    |		[bloom filter number of bits] 62353
                    |		[bloom filter number of hash functions] 5
             3124764|	[TsFileMetadataSize] 7858
             3124768|	[magic tail] TsFile
             3124774|	END of TsFile

---------------------------------- TsFile Sketch End ----------------------------------

到此已经介绍完了文件的整体结构,以及索引的改进过程。

那么一个sql是如何执行的呢,是怎样进行的查询?又是怎样高速的写入数据?

欢迎持续关注。。。。

posted @ 2021-01-28 16:10  Liutaohua  阅读(251)  评论(0编辑  收藏  举报