TsFile 的写入流程如下图所示:
其中,每个设备对应一个 ChunkGroupWriter,每个传感器对应一个 ChunkWriter。
文件的写入主要分为三种操作,在图上用 1、2、3 标注
TsFile 文件层的写入接口有两种
写入一个设备一个时间戳多个测点。
写入一个设备多个时间戳多个测点。
当调用 write 接口时,这个设备的数据会交给对应的 ChunkGroupWriter,其中的每个测点会交给对应的 ChunkWriter 进行写入。ChunkWriter 完成编码和打包(生成 Page)。
当内存中的数据达到一定阈值,会触发持久化操作。每次持久化会把当前内存中所有设备的数据全部持久化到磁盘的 TsFile 文件中。每个设备对应一个 ChunkGroup,每个测点对应一个 Chunk。
持久化完成后会在内存中缓存对应的元数据信息,以供查询和生成文件尾部 metadata。
根据内存中缓存的元数据,生成 TsFileMetadata 追加到文件尾部(TsFileWriter.flushMetadataIndex()
),最后关闭文件。
生成 TsFileMetadata 的过程中比较重要的一步是建立元数据索引 (MetadataIndex) 树。正如我们提到过的,元数据索引采用树形结构进行设计的目的是在设备数或者传感器数量过大时,可以不用一次读取所有的 TimeseriesMetadata
,只需要根据所读取的传感器定位对应的节点,从而减少 I/O,加快查询速度。以下是建立元数据索引树的详细算法和过程:
方法的输入包括:
整个方法分成三大部分:
deviceTimeseriesMetadataMap
中的每一个设备及其 TimeseriesMetadata
列表,转化为 MetadataIndexNode 并放进 deviceMetadataIndexMap
中。具体来说,对于每一个设备:queue
currentIndexNode
,类型为 LEAF_MEASUREMENT
MAX_DEGREE_OF_INDEX_NODE
个,加一条 entry 到 currentIndexNode
中MAX_DEGREE_OF_INDEX_NODE
个 entry 后,将 currentIndexNode
加入 queue
中,并将 currentIndexNode
指向一个新的 MetadataIndexNodequeue
中已经存储的叶子节点,逐层生成上层节点,直至最终的根节点(此方法解析见下),并将“设备-根节点”对应的映射加入 deviceMetadataIndexMap
中MAX_DEGREE_OF_INDEX_NODE
,如果未超过则可以直接形成元数据索引树的根节点并返回metadataIndexNode
,类型为 INTERNAL_MEASUREMENT
deviceMetadataIndexMap
中的每一个 entry:metadataIndexNode
中endOffset
并返回MAX_DEGREE_OF_INDEX_NODE
,则需要形成元数据索引树的设备索引层级queue
currentIndexNode
,类型为 LEAF_DEVICE
deviceMetadataIndexMap
中的每一个 entry:metadataIndexNode
中MAX_DEGREE_OF_INDEX_NODE
个 entry 后,将 currentIndexNode
加入 queue
中,并将 currentIndexNode
指向一个新的 MetadataIndexNodequeue
中已经存储的叶子节点,逐层生成上层节点,直至最终的根节点(此方法解析见下)endOffset
并返回方法的输入包括:
该方法需要将队列中的 MetadataIndexNode 形成树级结构,并返回根节点:
type
初始化 currentIndexNode
currentIndexNode
中MAX_DEGREE_OF_INDEX_NODE
个 entry 后,将 currentIndexNode
加入 queue
中,并将 currentIndexNode
指向一个新的 MetadataIndexNode方法的输入包括:
该方法直接设定当前节点的 endOffset,并将该节点加入队列中。