Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents

TsFile 文件格式

1. TsFile 设计

本章是关于 TsFile 的设计细节。

1.1 变量的存储

...

    比如: int0x8 将会被存储为 00 00 00 08, 而不是 08 00 00 00
  • 可变长的字符串类型

    • 存储的方式是以一个 int 类型的 Size + 字符串组成。Size 的值可以为 0。

    • Size 指的是字符串所占的字节数,它并不一定等于字符串的长度。

    • 举例来说,"sensor_1" 这个字符串将被存储为 00 00 00 08 + "sensor_1" (ASCII编码)。

    • 另外需要注意的一点是文件签名 "TsFile000001" (Magic String + Version), 因为他的 Size(12) 和 ASCII 编码值是固定的,所以没有必要在这个字符串前的写入 Size 值。

  • 数据类型

    • 0: BOOLEAN

    • 1: INT32 (int)

    • 2: INT64 (long)

    • 3: FLOAT

    • 4: DOUBLE

    • 5: TEXT (String)

  • 编码类型

    为了提高数据的存储效率,需要在数据写入的过程中对数据进行编码,从而减少磁盘空间的使用量。在写数据以及读数据的过程中都能够减少I/O操作的数据量从而提高性能。IoTDB支持多种针对不同类型的数据的编码方法:
    • 0: PLAIN

      • PLAIN编码,默认的编码方式,即不编码,支持多种数据类型,压缩和解压缩的时间效率较高,但空间存储效率较低。

    • 1: DICTIONARY

      • 字典编码是一种无损编码。它适合编码基数小的数据(即数据去重后唯一值数量小)。不推荐用于基数大的数据。
    • 2: RLE

    • 3: DIFF

    • 4: TS_2DIFF

    • 5: BITMAP

    • 6: GORILLA_V1

    • 7: REGULAR

    • 8: GORILLA

  • 压缩类型

    • 0: UNCOMPRESSED

    • 1: SNAPPY

    • 2: GZIP

    • 3: LZO

    • 4: SDT

    • 5: PAA

    • 6: PLA

    • 7: LZ4

1.2 TsFile 概述

<!-- TODO

下图是关于TsFile的结构图。

Image Removed

此文件包括两个设备 d1、d2,每个设备包含两个测点 s1、s2,共 4 个时间序列。每个时间序列包含两个 Chunk。

-->

      • 游程编码,比较适合存储某些整数值连续出现的序列,不适合编码大部分情况下前后值不一样的序列数据。

      • 游程编码也可用于对浮点数进行编码,但在创建时间序列的时候需指定保留小数位数(MAX_POINT_NUMBER,具体指定方式参见本文SQL 参考文档)。比较适合存储某些浮点数值连续出现的序列数据,不适合存储对小数点后精度要求较高以及前后波动较大的序列数据。

        游程编码(RLE)和二阶差分编码(TS_2DIFF)对 float 和 double 的编码是有精度限制的,默认保留2位小数。推荐使用 GORILLA。

    • 3: DIFF

    • 4: TS_2DIFF

      • 二阶差分编码,比较适合编码单调递增或者递减的序列数据,不适合编码波动较大的数据。
    • 5: BITMAP

    • 6: GORILLA_V1

      • GORILLA编码是一种无损编码,它比较适合编码前后值比较接近的数值序列,不适合编码前后波动较大的数据。

      • 当前系统中存在两个版本的GORILLA编码实现,推荐使用GORILLA,不推荐使用GORILLA_V1(已过时)。

      • 使用限制:使用Gorilla编码INT32数据时,需要保证序列中不存在值为Integer.MIN_VALUE的数据点;使用Gorilla编码INT64数据时,需要保证序列中不存在值为Long.MIN_VALUE的数据点。

    • 7: REGULAR

    • 8: GORILLA

    • 9: ZigZag

      • ZigZag编码将有符号整型映射到无符号整型,适合比较小的整数
  • 数据类型与支持编码的对应关系

    数据类型支持的编码
    BOOLEANPLAIN, RLE
    INT32PLAIN, RLE, TS_2DIFF, GORILLA, ZigZag
    INT64PLAIN, RLE, TS_2DIFF, GORILLA, ZigZag
    FLOATPLAIN, RLE, TS_2DIFF, GORILLA
    DOUBLEPLAIN, RLE, TS_2DIFF, GORILLA
    TEXTPLAIN, DICTIONARY


1.2 TsFile 概述

TsFile 整体分为两大部分:数据区索引区

数据区所包含的概念由小到大有如下三个:

...

一个 Chunk 存储了一个物理量(Measurement) 一段时间的数据,Chunk 内数据是按时间递增序存储的。Chunk 是由一个字节的分隔符 0x01, 一个 ChunkHeader 和若干个 Page 构成。

ChunkHeader 数据块头
成员类型解释
measurementIDString传感器名称
dataSizeintchunk 大小
dataTypeTSDataTypechunk的数据类型
compressionTypeCompressionType压缩类型
encodingTypeTSEncoding编码类型
numOfPagesint包含的page数量
Page 数据页

一个 Page 页存储了一段时间序列,是数据块被反序列化的最小单元。 它包含一个 PageHeader 和实际的数据(time-value 编码的键值对)。

PageHeader 结构:

成员类型解释
uncompressedSizeint压缩前数据大小
compressedSizeintSNAPPY压缩后数据大小
statisticsStatistics统计量

这里是statistics的详细信息:

成员描述DoubleStatisticsFloatStatisticsIntegerStatisticsLongStatisticsBinaryStatisticsBooleanStatistics
count数据点个数longlonglonglonglonglong
startTime开始时间longlonglonglonglonglong
endTime结束时间longlonglonglonglonglong
minValue最小值doublefloatintlong--
maxValue最大值doublefloatintlong--
firstValue第一个值doublefloatintlongBinaryboolean
lastValue最后一个值doublefloatintlongBinaryboolean
sumValuedoubledoubledoubledouble--
extreme极值doublefloatintlong--
ChunkGroupFooter 数据块组结尾
成员类型解释
entityIDString实体名称
dataSizelongChunkGroup 大小
numberOfChunksint包含的 chunks 的数量

1.2.3 索引区

1.2.3.1 ChunkIndex 数据块索引

第一部分的索引是 ChunkIndex

成员类型解释
measurementUidString传感器名称
offsetOfChunkHeaderlong文件中 ChunkHeader 开始的偏移量
tsDataTypeTSDataType数据类型
statisticsStatistics统计量
1.2.3.2 TimeseriesIndex 时间序列索引

第二部分的索引是 TimeseriesIndex

成员类型解释
measurementUidString物理量名称
tsDataTypeTSDataType数据类型
startOffsetOfChunkIndexListlong文件中 ChunkIndex 列表开始的偏移量
ChunkIndexListDataSizeintChunkIndex 列表的大小
statisticsStatistics统计量
1.2.3.3 IndexOfTimeseriesIndex 时间序列索引的索引(二级索引)

第三部分的索引是 IndexOfTimeseriesIndex

成员类型解释
IndexTreeIndexNode索引节点
offsetOfIndexArealong索引区的偏移量
bloomFilterBloomFilter布隆过滤器

索引节点 (IndexNode) 的成员和类型具体如下:

成员类型解释
childrenList<IndexEntry>节点索引项列表
endOffsetlong此索引节点的结束偏移量
nodeTypeIndexNodeType节点类型

索引项 (MetadataIndexEntry) 的成员和类型具体如下:

成员类型解释
nameString对应实体或物理量的名字
offsetlong偏移量

所有的索引节点构成一棵类B+树结构的索引树(二级索引),这棵树由两部分组成:实体索引部分和物理量索引部分。索引节点类型有四种,分别是INTERNAL_ENTITYLEAF_ENTITYINTERNAL_MEASUREMENTLEAF_MEASUREMENT,分别对应实体索引部分的中间节点和叶子节点,和物理量索引部分的中间节点和叶子节点。 只有物理量索引部分的叶子节点(LEAF_MEASUREMENT) 指向 TimeseriesIndex

...

索引树节点的度(即每个节点的最大子节点个数)可以由用户进行配置,配置项为max_degree_of_index_node,其默认值为256。在以下例子中,我们假定 max_degree_of_index_node = 10

需要注意的是,在索引树的每类节点(ENTITY、MEASUREMENT)中,键按照字典序排列。在下面的例子中,若i<j,假设字典序di<dj。(否则,实际上[d1,d2,...d10]的字典序排列应该为[d1,d10,d2,...d9])

其中,例1~4为一元时间序列的例子,例5~6为多元时间序列的例子,例7为综合例子。

...

在150个实体,每个实体中有150个物理量的情况下,物理量和实体个数均超过了 max_degree_of_index_node,形成索引树的物理量层级和实体索引层级。在这两个层级里,每个 IndexNode 均最多由10个 IndexEntry 组成。如前所述,从根节点到实体索引层级的叶子节点,类型分别为INTERNAL_ENTITYLEAF_ENTITY,而每个实体索引层级的叶子节点都是物理量索引层级的根节点,从这里到物理量索引层级的叶子节点,类型分别为INTERNAL_MEASUREMENTLEAF_MEASUREMENT

  • 例5:1个实体,20个物理量,2个多元时间序列组,每个多元时间序列组分别有9个物理量例5:1个实体,18个物理量,2个多元时间序列组,每个多元时间序列组分别有9个物理量

Image Modified

  • 例6:1个实体,30个物理量,2个多元时间序列组,每个多元时间序列组分别有15个物理量

  • 例7:2个实体,每个实体的物理量如下表所示

d0d1
【一元时间序列】s0,s1...,s4【一元时间序列】s0,s1,...s14
【多元时间序列】v0.(s5,s6,...,s14)【多元时间序列】v0.(s15,s16,..s18)
【一元时间序列】z15,z16,..,z18

索引采用树形结构进行设计的目的是在实体数或者物理量数量过大时,可以不用一次读取所有的 TimeseriesIndex,只需要根据所读取的物理量定位对应的节点,从而减少 I/O,加快查询速度。有关 TsFile 的读流程将在本章最后一节加以详细说明。

...

  • 注意: 如果没有设置输出文件的存储路径, 将使用 "TsFile_sketch_view.txt" 做为默认值。

在mac系统中的示例:

/iotdb/server/target/iotdb-server-{version}/tools/tsfileToolSet$ ./print-tsfile-sketch.sh test.tsfile
|````````````````````````
Starting Printing the TsFile Sketch
|````````````````````````
TsFile path:test.tsfile
Sketch save path:TsFile_sketch_view.txt
-------------------------------- TsFile Sketch --------------------------------
file path: test.tsfile
file length:

...

15462
14:40:55.619 [main] INFO org.apache.iotdb.tsfile.read.TsFileSequenceReader - Start reading file test.tsfile metadata from 15356, length 96

          POSITION| CONTENT
          -------- -------
               

...

  0|  

...

 [magic head] TsFile
                 

...

6|   

...

[version number] 

...

3
||||||||||||||||||||| [Chunk Group] of root.

...

sg_

...

1.

...

d1, num of Chunks:

...

4
                  7| [Chunk Group Header]
   

...

               |       [marker] 0
                  | [deviceID] root.sg_1.d1
           

...

  

...

  

...

 

...

21| 

...

 

...

 [

...

Chunk] of s6, numOfPoints:1000, time range:[0,999], tsDataType:INT64, 
                    startTime: 0 endTime: 999 count: 1000 [minValue:6,maxValue:9996,firstValue:6,lastValue:9996,sumValue:5001000.0]
                  | [chunk

...

header] marker=5, measurementId=s6, dataSize=1826, serializedSize=9
                  | [chunk] java.nio.HeapByteBuffer[pos=0 lim=1826 cap=1826]
                  |

...

 

...

   [page]  CompressedSize:1822, UncompressedSize:1951
              1856|

...

 [Chunk] of 

...

s4, numOfPoints:

...

1000, time range:[

...

0,

...

999], tsDataType:INT64, 
                 

...

     

...

startTime: 

...

0 endTime: 

...

999 count: 

...

1000 [minValue:

...

4,maxValue:

...

9994,firstValue:

...

4,lastValue:

...

9994,sumValue:

...

4999000.0]
                  | [chunk

...

header] marker=5, measurementId=s4, dataSize=1826, serializedSize=9
                  | [chunk]

...

java.nio.HeapByteBuffer[pos=0 lim=1826 cap=1826]
                  | [page]

...

CompressedSize:1822, UncompressedSize:1951
              3691|

...

[Chunk] of 

...

s2, numOfPoints:

...

1000, time range:[

...

0,

...

999], tsDataType:INT64, 
               

...

       startTime: 

...

0 endTime: 

...

999 count: 

...

1000 [minValue:

...

3,maxValue:

...

9993,firstValue:

...

3,lastValue:

...

9993,sumValue:

...

4998000.0]
                  | [chunk header]

...

marker=5, measurementId=s2, dataSize=1826, serializedSize=9
                  |

...

[chunk] java.nio.HeapByteBuffer[pos=0 lim=1826 cap=1826]
                  | [page]

...

CompressedSize:1822, UncompressedSize:1951
              5526|

...

[Chunk

...

]

...

 of s5, numOfPoints:1000, 

...

time range:[0,999], tsDataType:INT64, 
                    startTime: 0 endTime: 999 count: 1000 [minValue:5,maxValue:9995,firstValue:5,lastValue:9995,sumValue:5000000.0]
                  | [chunk header] marker=5, measurementId=s5, dataSize=1826, serializedSize=9
                  |

...

 [chunk] 

...

java.nio.HeapByteBuffer[pos=0 lim=1826 cap=1826]
                  | [page] CompressedSize:1822, UncompressedSize:1951
||||||||||||||||||||| [

...

Chunk Group] of 

...

root.sg_1.d1 ends
||||||||||||||||||||| [Chunk Group] of root.

...

sg_

...

1.d2, 

...

num of Chunks:4
              7361| [Chunk

...

Group Header]
                  |

...

[marker] 

...

0
                  |

...

[deviceID] root.sg_1.d2
              7375| [Chunk] of

...

s2, numOfPoints:

...

1000, time range:[

...

0,

...

999], tsDataType:

...

INT64, 
               

...

       startTime: 

...

0 endTime: 

...

999 count: 

...

1000 [minValue:

...

3,maxValue:

...

9993,firstValue:

...

3,lastValue:

...

9993,sumValue:

...

4998000.0]
                  | [chunk

...

header] marker=5, measurementId=s2, dataSize=1826, serializedSize=9
                  | [chunk]

...

java.nio.HeapByteBuffer[pos=0 lim=1826 cap=1826]
                  | [page]

...

CompressedSize:1822, UncompressedSize:1951
             

...

9210|   

...

[Chunk] of 

...

s4, numOfPoints:

...

1000, time range:[

...

0,

...

999], tsDataType:

...

INT64, 
               

...

       

...

startTime: 

...

0 endTime: 

...

999 count: 

...

1000 [minValue:

...

4,maxValue:

...

9994,firstValue:

...

4,lastValue:

...

9994,sumValue:

...

4999000.0]
                  | [chunk

...

header] marker=5, measurementId=s4, dataSize=1826, serializedSize=9
                  | [chunk]

...

java.nio.HeapByteBuffer[pos=0 lim=1826 cap=1826]
                  | [page]

...

CompressedSize:1822, UncompressedSize:1951
              11045| [Chunk] of

...

s6, numOfPoints:

...

1000, time range:[

...

0,

...

999], tsDataType:

...

INT64, 
                 

...

     

...

startTime: 

...

0 endTime: 

...

999 count: 

...

1000 [minValue:

...

6,maxValue:

...

9996,firstValue:

...

6,lastValue:

...

9996,sumValue:

...

5001000.0]
                  | [chunk

...

header] marker=5, measurementId=s6, dataSize=1826, serializedSize=9
                  | [chunk]

...

java.nio.HeapByteBuffer[pos=0 lim=1826 cap=1826]
                  | [page]

...

CompressedSize:1822, UncompressedSize:1951
              12880| [Chunk] of s5, numOfPoints:1000, time

...

range:[0,999], tsDataType:INT64, 
                    startTime: 0 endTime: 999

...

count: 1000 [minValue:5,maxValue:9995,firstValue:5,lastValue:9995,sumValue:5000000.0]
                  | [chunk

...

header] marker=5, measurementId=s5, dataSize=1826, serializedSize=9
                  | [chunk]

...

java.nio.HeapByteBuffer[pos=0 lim=1826 cap=1826]
                  |

...

[page]  CompressedSize:1822, UncompressedSize:1951
||||||||||||||||||||| [Chunk Group] of root.

...

sg_

...

1.

...

d2 ends
              14715| [marker] 2
       

...

  

...

    14716|   [TimeseriesIndex] of root.sg_1.d1.s2, tsDataType:INT64
                  |

...

     [

...

ChunkIndex]

...

 s2, offset=3691
                  | [startTime: 0

...

endTime: 999 count: 

...

1000 [minValue:3,maxValue:9993,firstValue:3,lastValue:9993,sumValue:4998000.0]] 
              14788| [TimeseriesIndex] of root.

...

sg_

...

1.

...

d1.s4, 

...

tsDataType:INT64
                  |

...

 

...

 

...

[ChunkIndex] s4, offset=1856
                  |

...

[startTime: 

...

0 endTime: 

...

999 count: 

...

1000 [minValue:4,maxValue:9994,firstValue:

...

4,lastValue:

...

9994,sumValue:4999000.0]] 
              14860| [TimeseriesIndex] of root.sg_1.d1.s5, tsDataType:INT64
         

...

      

...

   |       [ChunkIndex] s5, offset=5526
                  |

...

    [startTime: 0 endTime: 999 count: 1000 [minValue:5,maxValue:9995,firstValue:5,lastValue:9995,sumValue:5000000.0]] 
             

...

14932|   

...

[TimeseriesIndex] of root.sg_1.d1.s6, tsDataType:INT64
                  | [ChunkIndex] s6, offset=21
                  |

...

[startTime: 

...

0 endTime: 

...

999 count: 

...

1000 [minValue:6,maxValue:9996,firstValue:

...

6,lastValue

...

:9996,sumValue:5001000.0]] 
              15004| [TimeseriesIndex] of root.sg_1.d2.s2, tsDataType:INT64
         

...

      

...

  

...

 |       [ChunkIndex] s2, offset=7375
                  |

...

    [startTime: 0 endTime: 999 count: 1000 [minValue:3,maxValue:9993,firstValue:3,lastValue:9993,sumValue:4998000.0]] 
              15076|

...

  [TimeseriesIndex] of root.sg_1

...

.d2.s4, tsDataType:INT64
                  |

...

  [

...

ChunkIndex] 

...

s4, offset=9210
                  | [startTime: 0

...

endTime: 999 count: 1000 [minValue:4,maxValue:9994,firstValue:4,lastValue:9994,sumValue:4999000.0]] 
              15148| [TimeseriesIndex] of root.sg_1.d2.s5, tsDataType:INT64
                  | [ChunkIndex] s5, offset=12880
                  |

...

 

...

   [startTime: 0 endTime: 999 count: 1000 [minValue:5,maxValue:9995,firstValue:5,lastValue:9995,sumValue:5000000.0]] 
             

...

15220|   [

...

TimeseriesIndex] of root.

...

sg_

...

1.d2.s6, tsDataType:INT64
                 

...

| 

...

 

...

     [ChunkIndex] s6, offset=11045
                 

...

|  

...

 

...

    [startTime: 0 endTime: 999 count: 

...

1000 [minValue:6,maxValue:9996,firstValue:6,lastValue:9996,sumValue:5001000.0]] 
|||||||||||||||||||||

...


              15292| [IndexOfTimerseriesIndex Node] type=LEAF_MEASUREMENT
                  | <s2, 14716>
                  | <s6, 14932>
   

...

  

...

  

...

  

...

  

...

  

...

     |       <endOffset, 15004>
             

...

15324|   [

...

IndexOfTimerseriesIndex Node] 

...

type=LEAF_MEASUREMENT
                  | <s2, 15004>

...

      

...

             |       <s6, 15220>
     

...

             

...

| 

...

      <endOffset, 15292>
             

...

15356| 

...

 

...

 

...

[TsFileMetadata]
                  |

...

[meta offset] 

...

14715
                  | [num

...

of devices] 2
                  |

...

2 key&TsMetadataIndex
                  | [bloom filter

...

bit vector byte 

...

array length] 

...

32
                  | [bloom filter bit

...

vector 

...

byte 

...

array] 
                  | [bloom filter

...

number of 

...

bits] 

...

256
                  | [bloom filter

...

number of hash 

...

functions] 

...

5
              15452| [TsFileMetadataSize] 96
              15456| [magic tail] TsFile
              15462| END of TsFile
---------------------------- IndexOfTimerseriesIndex Tree -----------------------------
[MetadataIndex:LEAF_DEVICE]
└───[root.sg_1.d1,15292]
[MetadataIndex:LEAF_MEASUREMENT]

...

   └───[

...

s2,14716]

...


└───[s6,14932]

...

└───[root.sg_1.d2,15324]
[MetadataIndex:LEAF_MEASUREMENT]

...

   

...

 └───[s2,15004]

...

└───[s6,15220]]
---------------------------------- TsFile Sketch End ----------------------------------

...


3.4 TsFileSequenceRead

您可以使用示例中的类 example/tsfile/org/apache/iotdb/tsfile/TsFileSequenceRead 顺序打印 TsFile 中的内容.

...

clear all;close all;

% 1. load visdata generated by TsFileExtractVisdata
filePath = 'D:\visdata1.csv';
[timeMap,countMap] = loadVisData(filePath,'ms'); % mind the timestamp unit

% 2. plot figures given the loaded data and two plot parameters:
% `showSpecific` and `isFileOrder`
draw(timeMap,countMap,{},false)
title("draw(timeMap,countMap,\{\},false)")

draw(timeMap,countMap,{},true)
title("draw(timeMap,countMap,\{\},true)")

draw(timeMap,countMap,{'root.vehicle.d0.s0'},false)
title("draw(timeMap,countMap,{'root.vehicle.d0.s0'},false)")

draw(timeMap,countMap,{'root.vehicle.d0.s0','root.vehicle.d0.s1'},false)
title("draw(timeMap,countMap,{'root.vehicle.d0.s0','root.vehicle.d0.s1'},false)")

draw(timeMap,countMap,{'root.vehicle.d0.s0','root.vehicle.d0.s1'},true)
title("draw(timeMap,countMap,{'root.vehicle.d0.s0','root.vehicle.d0.s1'},true)")

绘图结果:

...

.vehicle.d0.s1'},true)
title("draw(timeMap,countMap,{'root.vehicle.d0.s0','root.vehicle.d0.s1'},true)")

绘图结果:

1Image Added2Image Added3Image Added4Image Added

附录

  • 大端存储

    • 比如: int0x8 将会被存储为 00 00 00 08, 而不是 08 00 00 00

  • 可变长的字符串类型

    • 存储的方式是以一个 int 类型的 Size + 字符串组成。Size 的值可以为 0。

    • Size 指的是字符串所占的字节数,它并不一定等于字符串的长度。

    • 举例来说,"sensor_1" 这个字符串将被存储为 00 00 00 08 + "sensor_1" (ASCII编码)。

    • 另外需要注意的一点是文件签名 "TsFile000001" (Magic String + Version), 因为他的 Size(12) 和 ASCII 编码值是固定的,所以没有必要在这个字符串前的写入 Size 值。

  • 压缩类型

    • 0: UNCOMPRESSED

    • 1: SNAPPY

    • 2: GZIP

    • 3: LZO

    • 4: SDT

    • 5: PAA

    • 6: PLA

    • 7: LZ4