blob: a3f62d9c6ad1015e3e1ff13b76419fc0c0426eef [file] [log] [blame]
(window.webpackJsonp=window.webpackJsonp||[]).push([[1075],{1634:function(e,t,l){"use strict";l.r(t);var n=l(69),_=Object(n.a)({},(function(){var e=this,t=e.$createElement,l=e._self._c||t;return l("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[l("h1",{attrs:{id:"tsfilemanagement-热合并"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#tsfilemanagement-热合并"}},[e._v("#")]),e._v(" TsFileManagement -热合并")]),e._v(" "),l("h2",{attrs:{id:"整体思路"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#整体思路"}},[e._v("#")]),e._v(" 整体思路")]),e._v(" "),l("p",[e._v("现在 iotdb 会因为自动参数优化将很小的数据块写入文件,使得数据文件变得零散化,这导致了用户进行即席查询时需要的读盘次数过多,而后期的合并过程又不是即时进行的,这导致了系统对热数据的查询效率变慢,所以我们借鉴了虚拟内存的思想,在写入流程中增加了热合并过程,通过提高一部分写放大保证了写入到正式文件的数据块不小于给定阈值,提升系统即席查询的效率。将这部分逻辑写到 StorageGroupProcessor 里去,针对封口的 tsfile 文件进行合并。")]),e._v(" "),l("ul",[l("li",[e._v("配置修改\n"),l("ul",[l("li",[e._v("iotdb-engine.properties 加一个参数 tsfile_manage_strategy:表示 tsfile 文件管理策略")]),e._v(" "),l("li",[e._v("tsfile_manage_strategy 内置两个策略:LevelStsrategy 和 NormalStrategy")]),e._v(" "),l("li",[e._v("LevelStrategy 即开启热合并并使用层级合并方法,NormalStrategy 即关闭热合并")]),e._v(" "),l("li",[e._v("iotdb-engine.properties 中加 merge_chunk_point_number:最大的chunk大小限制,LevelStrategy 时,当达到该参数就合并到最后一层")]),e._v(" "),l("li",[e._v("iotdb-engine.properties 加一个参数 max_level_num:LevelStrategy 时最大层数")])])]),e._v(" "),l("li",[e._v("代码结构修改\n"),l("ul",[l("li",[e._v("新建一个 TsFileManagement 类,专门管理 StorageGroupProcessor 中的 seqFileList 和 unSeqFileList ,在这里写热合并的主体逻辑,对外抽象对seqFileList和unSeqFileList的一系列所需接口")]),e._v(" "),l("li",[e._v("对于普通 merge 会调用 TsFileManagement 的 getStableTsFileList() 来获取准备进行文件合并的文件列表,这里对于 LevelStrategy 来说就是返回第 {max_level_num - 1} 层的文件,对于 NormalStrategy 来说就是返回所有文件列表")]),e._v(" "),l("li",[e._v("每一次热合并会取第一个被合并文件的时间戳作为新文件的时间戳,即 {firstFileTimestamp}-{version}-{mergeVerion + 1}.tsfiles")])])])]),e._v(" "),l("h2",{attrs:{id:"tsfilemanagement-对外提供的接口和类"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#tsfilemanagement-对外提供的接口和类"}},[e._v("#")]),e._v(" TsFileManagement 对外提供的接口和类")]),e._v(" "),l("ul",[l("li",[e._v("TsFileManagement 也管理未封口文件")]),e._v(" "),l("li",[e._v("TsFileManagement 同时管理 Seq 和 UnSeq 文件列表,在 StorageGroupProcessor 中只创建一个 TsFileManagement")]),e._v(" "),l("li",[e._v("List<TsFileResource> getStableTsFileList() 对外提供稳定的 TsFileResource 列表")]),e._v(" "),l("li",[e._v("List<TsFileResource> getTsFileList(boolean sequence) 对外提供(顺序/乱序)文件列表(如果 sequence = true,则提供按时间戳顺序的列表)")]),e._v(" "),l("li",[e._v("Iterator<TsFileResource> getIterator(boolean sequence) 对外提供(顺序/乱序)文件迭代器(如果 sequence = true,则提供按时间戳顺序的迭代器)")]),e._v(" "),l("li",[e._v("void remove(TsFileResource tsFileResource, boolean sequence) 删除对应的 TsFileResource 文件")]),e._v(" "),l("li",[e._v("void removeAll(List<TsFileResource> tsfileReourceList, boolean sequence) 删除对应的 TsFileResource 列表")]),e._v(" "),l("li",[e._v("void addAll(List<TsFileResource> tsfileReourceList, boolean sequence) 批量加入 TsFileResource 列表")]),e._v(" "),l("li",[e._v("boolean isEmpty(boolean sequence) 是否为空")]),e._v(" "),l("li",[e._v("int size(boolean sequence) 对应的文件列表长度")]),e._v(" "),l("li",[e._v("void forkCurrentFileList(long timePartition) 保存当前文件的某时间分区列表")]),e._v(" "),l("li",[e._v("void recover() 调用对应的恢复流程")]),e._v(" "),l("li",[e._v("HotCompactionMergeTask 调用对应的异步合并流程,传入 closeUnsealedTsFileProcessorCallBack 以通知外部类合并结束")])]),e._v(" "),l("h2",{attrs:{id:"levelstrategy-merge-流程"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#levelstrategy-merge-流程"}},[e._v("#")]),e._v(" LevelStrategy merge 流程")]),e._v(" "),l("ul",[l("li",[e._v("外部调用 forkCurrentFileList(long timePartition) 保存当前文件的某时间分区列表\n"),l("ul",[l("li",[e._v("这里选择的文件列表 chunk 点数之和不超出 merge_chunk_point_number")])])]),e._v(" "),l("li",[e._v("外部异步创建并提交 merge 线程")]),e._v(" "),l("li",[e._v("判断是否要进行全局热合并\n"),l("ul",[l("li",[e._v("生成目标文件 {first_file_name}-{max_level_num - 1}.tsfile")]),e._v(" "),l("li",[e._v("生成合并日志 .hot_compaction.log")]),e._v(" "),l("li",[e._v("记录是全局合并")]),e._v(" "),l("li",[e._v("记录目标文件")]),e._v(" "),l("li",[e._v("进行合并(记录日志 device - offset)")]),e._v(" "),l("li",[e._v("记录完成合并")])])]),e._v(" "),l("li",[e._v("循环判断每一层的文件是否要合并到下一层\n"),l("ul",[l("li",[e._v("生成目标文件 {first_file_name}-{level + 1}.tsfile")]),e._v(" "),l("li",[e._v("生成合并日志 .hot_compaction.log")]),e._v(" "),l("li",[e._v("记录是层级合并")]),e._v(" "),l("li",[e._v("记录目标文件")]),e._v(" "),l("li",[e._v("进行合并(记录日志 device - offset)")]),e._v(" "),l("li",[e._v("记录完成合并")])])]),e._v(" "),l("li",[e._v("加写锁")]),e._v(" "),l("li",[e._v("从磁盘删掉待合并的文件,并从正式文件列表中移除")]),e._v(" "),l("li",[e._v("删除合并日志 .hot_compaction.log")]),e._v(" "),l("li",[e._v("释放写锁")])]),e._v(" "),l("h2",{attrs:{id:"levelstrategy-recover-流程"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#levelstrategy-recover-流程"}},[e._v("#")]),e._v(" LevelStrategy recover 流程")]),e._v(" "),l("ul",[l("li",[e._v("如果日志文件存在\n"),l("ul",[l("li",[e._v("如果是全局合并(把所有小文件合并到最后一层)\n"),l("ul",[l("li",[e._v("如果合并没结束\n"),l("ul",[l("li",[e._v("截断文件")]),e._v(" "),l("li",[e._v("继续全局合并")])])])])]),e._v(" "),l("li",[e._v("如果是层级合并\n"),l("ul",[l("li",[e._v("如果合并没结束\n"),l("ul",[l("li",[e._v("截断文件")]),e._v(" "),l("li",[e._v("继续层级合并")])])])])])])]),e._v(" "),l("li",[e._v("如果日志文件不存在\n"),l("ul",[l("li",[e._v("无需恢复")])])])]),e._v(" "),l("h2",{attrs:{id:"levelstrategy-例子"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#levelstrategy-例子"}},[e._v("#")]),e._v(" LevelStrategy 例子")]),e._v(" "),l("p",[e._v("设置 max_file_num_in_each_level = 3,tsfile_manage_strategy = LevelStrategy, max_level_num = 3,此时文件结构为,第0层、第1层、第2层,其中第2层是不再做热合并的稳定的文件列表")]),e._v(" "),l("h3",{attrs:{id:"完全根据-level-合并的情况"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#完全根据-level-合并的情况"}},[e._v("#")]),e._v(" 完全根据 level 合并的情况")]),e._v(" "),l("p",[e._v("假设此时整个系统中有5个文件,最后一个文件没有关闭,则其结构及顺序分布如下\nlevel-0: t2-0 t3-0 t4-0\nlevel-1: t0-1 t1-1\n当最后一个文件关闭,按如下方式合并(第0层的t2-0、t3-0、t4-0文件合并到了第1层的t2-1文件)\nlevel-0: t2-0 t3-0 t4-0\n\\ \\ |\n\\ \\ |\n\\ \\ |\n\\ \\ |\nlevel-1: t0-1 t1-1 t2-1\n合并后发现第1层合并后也满了,则继续合并到第2层,最后整个系统只剩下了第2层的t0-2文件\nlevel-0: t2-0 t3-0 t4-0\n\\ \\ |\n\\ \\ |\n\\ \\ |\n\\ \\ |\nlevel-1: t0-1 t1-1 t2-1\n| / /\n| / /\n| / /\n| / /\nlevel-2: t0-2")]),e._v(" "),l("h3",{attrs:{id:"中途满足-merge-chunk-point-number-的情况"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#中途满足-merge-chunk-point-number-的情况"}},[e._v("#")]),e._v(" 中途满足 merge_chunk_point_number 的情况")]),e._v(" "),l("p",[e._v("假设此时整个系统中有4个文件,最后一个文件没有关闭,则其结构及顺序分布如下\nlevel-0: t2-0 t3-0\nlevel-1: t0-1 t1-1\n当最后一个文件关闭,但是t0-1、t1-1、t2-0、t3-0文件的 chunk point 数量加起来已经满足 merge_chunk_point_number,则做如下合并,即直接将所有文件合并到第2层(稳定层)\nlevel-0: t2-0 t3-0\n| |\nlevel-1: t0-1 t1-1\n| /\n| /\n| /\nlevel-2: t0-2")]),e._v(" "),l("h3",{attrs:{id:"动态参数调整的例子"}},[l("a",{staticClass:"header-anchor",attrs:{href:"#动态参数调整的例子"}},[e._v("#")]),e._v(" 动态参数调整的例子")]),e._v(" "),l("ul",[l("li",[e._v("系统会严格按照上文中提到的 merge 流程对多层文件进行选择和合并,这里只介绍到参数调整时,系统初始化过程中文件会被放在哪一层")]),e._v(" "),l("li",[e._v("假设max_file_num_in_each_level = 3")])]),e._v(" "),l("ul",[l("li",[l("p",[e._v("从0.10.0升级")]),e._v(" "),l("ul",[l("li",[e._v("将所有文件的 mergeVersion 置为 {max_level_num - 1}")]),e._v(" "),l("li",[e._v("即老版本的文件不会被重复合并\n假设整个系统中有5个文件,此时恢复后的文件结构为:\nlevel-2: t0-2 t1-2 t2-2 t3-2 t4-2")])])]),e._v(" "),l("li",[l("p",[e._v("提高 max_level_num")]),e._v(" "),l("ul",[l("li",[e._v("此时因为不会改变任何文件的原 level,所以 recover 时文件还会被放到原来的层上,或超出 {max_level_num - 1} 的文件被放在最后一层(考虑到多次调整的情况)")]),e._v(" "),l("li",[e._v("即原文件将基于原来的 level 继续合并,超出 {max_level_num - 1} 部分也不会有乱序问题,因为在最后一层的必然是老文件\n假设整个系统中有5个文件,原max_file_num_in_each_level = 2,提高后的max_file_num_in_each_level = 3,此时恢复后的文件结构为:\nlevel-0: t2-0 t3-0 t4-0\nlevel-1: t0-1 t1-1\n假设 {size(t2-0)+size(t3-0)+size(t4-0)< merge_chunk_point_number},则进行合并的过程如下\nlevel-0: t2-0 t3-0 t4-0\n\\ \\ |\n\\ \\ |\n\\ \\ |\n\\ \\ |\nlevel-1: t0-1 t1-1 t2-1\n合并后发现第1层合并后也满了,则继续合并到第2层,最后整个系统只剩下了第2层的t0-2文件\nlevel-0: t2-0 t3-0 t4-0\n\\ \\ |\n\\ \\ |\n\\ \\ |\n\\ \\ |\nlevel-1: t0-1 t1-1 t2-1\n| / /\n| / /\n| / /\n| / /\nlevel-2: t0-2")])])]),e._v(" "),l("li",[l("p",[e._v("降低 max_level_num")]),e._v(" "),l("ul",[l("li",[e._v("此时因为不会改变任何文件的原 level,所以 recover 时小于此时 {max_level_num - 1} 的文件还会被放到原来的层上,而超出的文件将被放在最后一层")]),e._v(" "),l("li",[e._v("即部分文件将被继续合并,而超出 {max_level_num - 2} 的文件将不会再被热合并\n假设整个系统中有7个文件,原max_file_num_in_each_level = 3,降低后的max_file_num_in_each_level = 2,此时恢复后的文件结构为:\nlevel-0: t4-0 t5-0 t6-0\nlevel-1: t0-2 t1-1 t2-1 t3-1\n假设 {size(t2-0)+size(t3-0)+size(t4-0)< merge_chunk_point_number},则进行合并的过程如下\nlevel-0: t2-0 t3-0 t4-0\n\\ \\ |\n\\ \\ |\n\\ \\ |\n\\ \\ |\nlevel-1: t0-2 t1-1 t2-1 t3-1 t4-1\n合并后发现第1层合并后也满了,则继续合并到第2层\nlevel-0: t2-0 t3-0 t4-0\n\\ \\ |\n\\ \\ |\n\\ \\ |\n\\ \\ |\nlevel-1: t0-2 t1-1 t2-1 t3-1 t4-1\n| / /\n| / /\n| / /\n| / /\nlevel-2: t0-2\n最后剩下的文件结构为\nlevel-0:\nlevel-1: t3-1 t4-1\nlevel-2: t0-2")])])]),e._v(" "),l("li",[l("p",[e._v("NormalStrategy -> LevelStrategy")]),e._v(" "),l("ul",[l("li",[e._v("此时因为因为删去了原始合并的 {mergeVersion + 1} 策略,所以所有文件将全部被放到0层")]),e._v(" "),l("li",[e._v("每一次热合并会最多取出满足 {merge_chunk_point_number} 的文件进行合并,直到将所有多余的文件热合并完,进入正常的热合并流程\n假设整个系统中有5个文件,此时恢复后的文件结构为:\nlevel-2: t0-0 t1-0 t2-0 t3-0 t4-0\n假设 {size(t0-0)+size(t1-0)>=merge_chunk_point_number},则进行第一次合并的过程如下\nlevel-0: t0-0 t1-0 t2-0 t3-0 t4-0 t5-0(新增了文件才会触发合并检查)\n| /\n| /\n| /\nlevel-2: t0-2\n假设 {size(t2-0)+size(t3-0)>=merge_chunk_point_number},则进行第二次合并的过程如下\nlevel-0: t2-0 t3-0 t4-0 t5-0 t6-0(新增了文件才会触发合并检查)\n\\ |\n\\ |\n\\ |\nlevel-2: t0-2 t2-2\n假设 {size(t4-0)+size(t5-0)+size(t6-0)+size(t7-0)< merge_chunk_point_number},则进行第三次合并的过程如下\nlevel-0: t4-0 t5-0 t6-0 t7-0(新增了文件才会触发合并检查)\n| / /\n| / /\n| / /\n| //"),l("br"),e._v("\nlevel-1: t4-1")])])])]),e._v(" "),l("p",[e._v("level-2: t0-2 t2-2")])])}),[],!1,null,null,null);t.default=_.exports}}]);