重启恢复是以存储组为粒度进行的,恢复的入口是 StorageGroupProcessor 的 recover()
org.apache.iotdb.db.engine.storagegroup.StorageGroupProcessor.recover()
首先获得该存储组下所有以.tsfile结尾的数据文件,返回 TsFileResource,共有如下几个文件列表
顺序文件
乱序文件
若该存储组下有 0.9 版本的 TsFile 文件,则将旧版本的顺序和乱序文件分别加入upgradeSeqFileList和upgradeSeqFileList中,供升级和查询使用。
将顺序、乱序文件按照分区分组 Map<Long, List>
恢复每个分区的顺序文件,将上一步获得的每个分区的顺序 TsFile 文件作为参数,调用recoverTsFiles进行恢复,该方法会将恢复后的顺序 TsFile 以TsFileResource 的形式放入sequenceFileTreeSet中,若该 TsFile 是此分区的最后一个,且未封口,则还要为其构造TsFileProcessor对象,并加入workSequenceTsFileProcessors中,该方法的具体细节会在下一小节阐述。
恢复每个分区的乱序文件,将上一步获得的每个分区的乱序 TsFile 文件作为参数,调用recoverTsFiles进行恢复,该方法会将恢复后的乱序 TsFile 以 TsFileResource 的形式放入unSequenceFileList中,若该 TsFile 是此分区的最后一个,且未封口,则还要为其构造TsFileProcessor对象,并加入workUnsequenceTsFileProcessors中,该方法的具体细节会在下一小节阐述。
分别遍历上两步得到的sequenceFileTreeSet和unSequenceFileList,更新分区对应的版本号
检查有没有merge时候的Modification文件,并调用RecoverMergeTask.recoverMerge方法对merge进行恢复
调用updateLastestFlushedTime()方法,用 0.9 版本的顺序tsfile文件,更新latestTimeForEachDevice, partitionLatestFlushedTimeForEachDevice以及globalLatestFlushedTimeForEachDevice
latestTimeForEachDevice 记录了所有device已经插入的各个分区下的最新的时间戳(包括未flush的和已flush的)partitionLatestFlushedTimeForEachDevice 记录了所有device已经flush的各个分区下的最新的时间戳,它用来判断一个新插入的点是不是乱序点globalLatestFlushedTimeForEachDevice 记录了所有device已经flush的最新时间戳(是各个分区的最新时间戳的汇总)最后遍历sequenceFileTreeSet,用恢复出来的顺序文件,再次更新latestTimeForEachDevice, partitionLatestFlushedTimeForEachDevice以及globalLatestFlushedTimeForEachDevice
该方法主要负责遍历传进来的所有 TsFile,挨个进行恢复。
构造出TsFileRecoverPerformer对象,对 TsFile 文件进行恢复,恢复的逻辑封装在TsFileRecoverPerformer的recover()方法中(具体细节在下一小节展开阐述),该方法会返回一个恢复后的RestorableTsFileIOWriter对象。
若该 TsFile 文件不是最后一个文件,或者该 TsFile 文件是最后一个文件,但已经被关闭或标记被关闭,只需将该 TsFile 文件在内存中对应的TsFileResource对象的closed属性置成true即可。
若该 TsFile 文件可以继续写入,则表示这是此分区的最后一个 TsFile,且未封口,则继续保持其未封口的状态,需要为它构造一个TsFileProcessor对象,并将其放到workSequenceTsFileProcessors或workUnsequenceTsFileProcessors中。
最后将恢复出来的 TsFile 文件在内存中对应的TsFileResource对象放入sequenceFileTreeSet或unSequenceFileList中
该方法主要负责每个具体的 TsFile 文件的恢复。
首先用tsfile文件构造出一个RestorableTsFileIOWriter对象,在RestorableTsFileIOWriter的构造方法中,会对该tsfile的文件内容进行检查,必要时进行截断
MAGIC_STRING和VERSION_NUMBER后,直接返回,此时的crashed为false,canWrite为true;TsFileSequenceReader对象对内容进行解析,调用selfCheck方法进行自检,并将不完整的内容截断,初始化truncatedSize为HeaderLengthMAGIC_STRING和VERSION_NUMBER,以及尾部的MAGIC_STRING),则返回TsFileCheckStatus.COMPLETE_FILEHeaderLength(len(MAGIC_STRING) + len(VERSION_NUMBER)),或者文件头部内容不是MAGIC_STRING,则返回INCOMPATIBLE_FILEHeaderLength,且文件内容就是MAGIC_STRING + VERSION_NUMBER,则返回HeaderLengthHeaderLength,且文件头合法,但文件尾部没有MAGIC_STRING,表示该文件不完整,需要进行截断。从VERSION_NUMBER往后读,读出chunk中的数据,并根据chunk中的数据恢复出ChunkMetadata,若遇到CHUNK_GROUP_FOOTER,则表示整个ChunkGroup是完整的,更新truncatedSize至当前位置truncatedSizetruncatedSize,对文件进行截断truncatedSize等于TsFileCheckStatus.COMPLETE_FILE,则将crashed和canWrite置为false,并关闭文件的输出流truncatedSize等于TsFileCheckStatus.INCOMPATIBLE_FILE,则关闭文件的输出流,并抛异常crashed和canWrite置为true,并将文件截断至truncatedSize通过返回的 RestorableTsFileIOWriter 判断文件是否完整
若该 TsFile 文件是完整的
RestorableTsFileIOWriter若 TsFile 不完整
recoverResourceFromWriter,通过RestorableTsFileIOWriter中的ChunkMetadata信息,恢复出resource信息redoLogs方法将这个文件对应的一个或多个写前日志文件中的数据都写到一个临时 Memtable 中,并持久化到这个不完整的 TsFile 中.closing文件存在,则调用RestorableTsFileIOWriter的endFile()方法,将文件封口,并删除.closing文件,并为其生成resource文件