存储层设计介绍1——存储结构设计解析
1 整体介绍
Doris是基于MPP架构的交互式SQL数据仓库,主要用于解决近实时的报表和多维分析。Doris高效的导入、查询离不开其存储结构精巧的设计。
本文主要通过阅读Doris BE模块代码,详细分析了Doris BE模块存储层的实现原理,阐述和解密Doris高效的写入、查询能力背后的核心技术。其中包括Doris列存的设计、索引设计、数据读写流程、Compaction流程、Tablet和Rowset的版本管理、数据备份等功能。这里会通过三篇文章来逐步进行介绍,分别为《Doris存储层设计介绍1——存储结构设计解析》、《Doris存储层设计介绍2——读写、compaction流程分析》、《Doris存储层设计介绍3——读取流程、Compaction流程分析》。
本文为第一篇《Doris存储层设计介绍1——存储结构设计解析》 ,文章介绍了Segment V2版本的存储层结构,包括了有序存储、稀疏索引、前缀索引、位图索引、BloomFilter等丰富功能,可以应对各种复杂的场景提供极速的查询能力。
2 设计目标
- 批量导入,少量更新
- 绝大多数的读请求
- 宽表场景,读取大量行,少量列
- 非事务场景
- 良好的扩展性
3 储存文件格式
3.1 存储目录结构
存储层对存储数据的管理通过storage_root_path路径进行配置,路径可以是多个。存储目录下一层按照分桶进行组织,分桶目录下存放具体的tablet,按照tablet_id命名子目录。
Segment文件存放在tablet_id目录下按SchemaHash管理。Segment文件可以有多个,一般按照大小进行分割,默认为256MB。其中,Segment v2文件命名规则为:${rowset_id}_${segment_id}.dat。
具体存储目录存放格式如下图所示:
3.2 Segment v2文件结构
Segment整体的文件格式分为数据区域,索引区域和footer三个部分,如下图所示:
- Data Region: 用于存储各个列的数据信息,这里的数据是按需分page加载的
- Index Region: Doris中将各个列的index数据统一存储在Index Region,这里的数据会按照列粒度进行加载,所以跟列的数据信息分开存储
- Footer信息
- SegmentFooterPB: 定义文件的元数据信息
- 4个字节的FooterPB内容的checksum
- 4个字节的FileFooterPB消息长度,用于读取FileFooterPB8个字节的MAGIC CODE,之所以在末位存储,是方便不同的场景进行文件类型的识别
下面分布介绍各个部分的存储格式的设计。
4 Footer信息
Footer信息段在文件的尾部,存储了文件的整体结构,包括数据域的位置,索引域的位置等信息,其中有SegmentFooterPB,CheckSum,Length,MAGIC CODE 4个部分。
SegmentFooterPB数据结构如下:
SegmentFooterPB采用了PB格式进行存储,主要包含了列的meta信息、索引的meta信息,Segment的short key索引信息、总行数。
4.1 列的meta信息
- ColumnId:当前列在schema中的序号
- UniqueId:全局唯一的id
- Type:列的类型信息
- Length:列的长度信息
- Encoding:编码格式
- Compression:压缩格式
- Dict PagePointer:字典信息
4.2 列索引的meta信息
-
OrdinalIndex:存放列的稀疏索引meta信息。
-
ZoneMapIndex:存放ZoneMap索引的meta信息,内容包括了最大值、最小值、是否有空值、是否没有非空值。SegmentZoneMap存放了全局的ZoneMap信息,PageZoneMaps则存放了每个页面的统计信息。
-
BitMapIndex:存放BitMap索引的meta信息,内容包括了BitMap类型,字典数据BitMap数据。
-
BloomFilterIndex:存放了BloomFilter索引信息。
为了防止索引本身数据量过大,ZoneMapIndex、BitMapIndex、BloomFilterIndex采用了两级的Page管理。对应了IndexColumnMeta的结构,当一个Page能够放下时,当前Page直接存放索引数据,即采用1级结构;当一个Page无法放下时,索引数据写入新的Page中,Root Page存储数据Page的地址信息。
5 Ordinal Index (一级索引)
Ordinal Index索引提供了通过行号来查找Column Data Page数据页的物理地址。Ordinal Index能够将按列存储数据按行对齐,可以理解为一级索引。其他索引查找数据时,都要通过Ordinal Index查找数据Page的位置。因此,这里先介绍Ordinal Index索引。
在一个segment中,数据始终按照key(AGGREGATE KEY、UNIQ KEY 和 DUPLICATE KEY)排序顺序进行存储,即key的排序决定了数据存储的物理结构。确定了列数据的物理结构顺序,在写入数据时,Column Data Page是由Ordinal index进行管理,Ordinal index记录了每个Column Data Page的位置offset、大小size和第一个数据项行号信息,即Ordinal。这样每个列具有按行信息进行快速扫描的能力。Ordinal index采用的稀疏索引结构,就像是一本书目录,记录了每个章节对应的页码。
5.1 存储结构
Ordinal index元信息存储在SegmentFooterPB中的每个列的OrdinalIndexMeta中。具体结构如下图所示:
在OrdinalIndexMeta中存放了索引数据对应的root page地址,这里做了一些优化,当数据仅有一个page时,这里的地址可以直接指向唯一的数据page;当一个page放不下时,指向OrdinalIndex类型的二级结构索引page,索引数据中每个数据项对应了Column Data Page offset位置、size大小和ordinal行号信息。其中Ordinal index索引粒度与page粒度一致,默认64*1024字节。
6 列数据存储
Column的data数据按照Page为单位分块存储,每个Page大小一般为64*1024个字节。Page在存储的位置和大小由ordinal index管理。
6.1 data page存储结构
DataPage主要为Data部分、Page Footer两个部分。
Data部分存放了当前Page的列的数据。当允许存在Null值时,对空值单独存放了Null值的Bitmap,由RLE格式编码通过bool类型记录Null值的行号。
Page Footer包含了Page类型Type、UncompressedSize未压缩时的数据大小、FirstOrdinal当前Page第一行的RowId、NumValues为当前Page的行数、NullMapSize对应了NullBitmap的大小。
6.2 数据压缩
针对不同的字段类型采用了不同的编码。默认情况下,针对不同类型采用的对应关系如下:
TINYINT/SMALLINT/INT/BIGINT/LARGEINT | BIT_SHUFFLE |
---|---|
FLOAT/DOUBLE/DECIMAL | BIT_SHUFFLE |
CHAR/VARCHAR | DICT |
BOOL | RLE |
DATE/DATETIME | BIT_SHUFFLE |
HLL/OBJECT | PLAIN |
默认采用LZ4F格式对数据进行压缩。
7 Short Key Index索引
7.1 存储结构
Short Key Index前缀索引,是在key(AGGREGATE KEY、UNIQ KEY 和 DUPLICATE KEY)排序的基础上,实现的一种根据给定前缀列,快速查询数据的索引方式。这里Short Key Index索引也采用了稀疏索引结构,在数据写入过程中,每隔一定行数,会生成一个索引项。这个行数为索引粒度默认为1024行,可配置。该过程如下图所示:
其中,KeyBytes中存放了索引项数据,OffsetBytes存放了索引项在KeyBytes中的偏移。
7.2 索引生成规则
Short Key Index采用了前36 个字节,作为这行数据的前缀索引。当遇到 VARCHAR 类型时,前缀索引会直接截断。
7.3 应用案例
(1)以下表结构的前缀索引为 user_id(8Byte) + age(4Bytes) + message(prefix 24 Bytes)。
ColumnName | Type |
---|---|
user_id | BIGINT |
age | INT |
message | VARCHAR(100) |
max_dwell_time | DATETIME |
min_dwell_time | DATATIME |
(2)以下表结构的前缀索引为 user_name(20 Bytes)。即使没有达到 36 个字节,因为遇到 VARCHAR,所以直接截断,不再往后继续。
Column | Type |
---|---|
user_name | VARCHAR(20) |
age | INT |
message | VARCHAR(100) |
max_dwell_time | DATETIME |
min_dwell_time | DATETIME |
当我们的查询条件,是前缀索引的前缀时,可以极大的加快查询速度。比如在第一个例子中,我们执行如下查询:
SELECT * FROM table WHERE user_id=1829239 and age=20;
该查询的效率会远高于如下查询:
SELECT * FROM table WHERE age=20;
所以在建表时,正确的选择列顺序,能够极大地提高查询效率。
8 ZoneMap Index索引
ZoneMap索引存储了Segment和每个列对应每个Page的统计信息。这些统计信息可以帮助在查询时提速,减少扫描数据量,统计信息包括了Min最大值、Max最小值、HashNull空值、HasNotNull不全为空的信息。
8.1 存储结构
ZoneMap索引存储结构如下图所示:
在SegmentFootPB结构中,每一列索引元数据ColumnIndexMeta中存放了当前列的ZoneMapIndex索引数据信息。ZoneMapIndex有两个部分,SegmentZoneMap和PageZoneMaps。SegmentZoneMap存放了当前Segment全局的ZoneMap索引信息,PageZoneMaps存放了每个Data Page的ZoneMap索引信息。
PageZoneMaps对应了索引数据存放的Page信息IndexedColumnMeta结构,目前实现上没有进行压缩,编码方式也为Plain。IndexedColumnMeta中的OrdinalIndexPage指向索引数据root page的偏移和大小,这里同样做了优化二级Page优化,当仅有一个DataPage时,OrdinalIndexMeta直接指向这个DataPage;有多个DataPage时,OrdinalIndexMeta先指向OrdinalIndexPage,OrdinalIndexPage是一个二级Page结构,里面的数据项为索引数据DataPage的地址偏移offset,大小Size和ordinal信息。
8.2 索引生成规则
Doris默认为key列开启ZoneMap索引;当表的模型为DUPULCATE时,会所有字段开启ZoneMap索引。在列数据写入Page时,自动对数据进行比较,不断维护当前Segment的ZoneMap和当前Page的ZoneMap索引信息。
8.3 应用案例
在数据查询时,会根据范围条件过滤的字段会按照ZoneMap统计信息选取扫描的数据范围。例如在案例1中,对age字段进行过滤。查询语句如下:
SELECT * FROM table WHERE age > 20 and age < 1000
在没有命中Short Key Index的情况下,会根据条件语句中age的查询条件,利用ZoneMap索引找到应该扫描的数据ordinary范围,减少要扫描的page数量。
9 BloomFilter
当一些字段不能利用Short Key Index并且字段存在区分度比较大时,Doris提供了BloomFilter索引。
9.1、存储结构
BloomFilter的存储结构如下图所示:
BloomFilterIndex信息存放了生产的Hash策略、Hash算法和BloomFilter过对应的数据Page信息。Hash算法采用了HASH_MURMUR3,Hash策略采用了BlockSplitBloomFilter分块实现策略,期望的误判率fpp默认配置为0.05。
BloomFilter索引数据对应数据Page的存放与ZoneMapIndex类似,做了二级Page的优化,这里不再详细阐述。
9.2、索引生成规则
BloomFilter按Page粒度生成,在数据写入一个完整的Page时,Doris会根据Hash策略同时生成这个Page的BloomFilter索引数据。目前bloom过滤器不支持tinyint/hll/float/double类型,其他类型均已支持。使用时需要在PROPERTIES中指定bloom_filter_columns要使用BloomFilter索引的字段。
9.3 应用案例
在数据查询时,查询条件在设置有bloom过滤器的字段进行过滤,当bloom过滤器没有命中时表示该Page中没有该数据,这样可以减少要扫描的page数量。
案例:table的schema如下:
ColumnName | Type |
---|---|
user_id | BIGINT |
age | INT |
name | VARCHAR(20) |
city | VARCHAR(200) |
createtime | DATETIME |
这里的查询sql如下:
SELECT * FROM table WHERE name = '张三'
由于name的区分度较大,为了提升sql的查询性能,对name数据增加了BloomFilter索引,PROPERTIES ( “bloom_filter_columns” = “name” )。在查询时通过BloomFilter索引能够大量过滤掉Page。
10 Bitmap Index索引
Doris还提供了BitmapIndex用来加速数据的查询。
10.1、存储结构
Bitmap存储格式如下:
BitmapIndex的meta信息同样存放在SegmentFootPB中,BitmapIndex包含了三部分,BitMap的类型、字典信息DictColumn、位图索引数据信息BitMapColumn。其中DictColumn、BitMapColumn都对应IndexedColumnData结构,分别存放了字典数据和索引数据的Page地址offset、大小size。这里同样做了二级page的优化,不再具体阐述。
这里与其他索引存储结构有差异的地方是DictColumn字典数据进行了LZ4F压缩,在记录二级Page偏移时存放的是Data Page中的第一个值。
10.2、索引生成规则
BitMap创建时需要通过 CREATE INDEX 进行创建。Bitmap的索引是整个Segment中的Column字段的索引,而不是为每个Page单独生成一份。在写入数据时,会维护一个map结构记录下每个key值对应的行号,并采用Roaring位图对rowid进行编码。主要结构如下:
生成索引数据时,首先写入字典数据,将map结构的key值写入到DictColumn中。然后,key对应Roaring编码的rowid以字节方式将数据写入到BitMapColumn中。
10.3、应用案例
在数据查询时,对于区分度不大,列的基数比较小的数据列,可以采用位图索引进行优化。比如,性别,婚姻,地理信息等。
案例:table的schema如下:
ColumnName | Type |
---|---|
user_id | BIGINT |
age | INT |
name | VARCHAR(20) |
city | VARCHAR(200) |
createtime | DATETIME |
这里的查询sql如下:
SELECT * FROM table WHERE city in ("北京", "上海")
由于city的取值比较少,建立数据字典和位图后,通过扫描位图便可以快速查找出匹配行。并且位图压缩后,数据量本身较小,通过扫描较少数据便能够对整个列进行精确的匹配。
11 索引的查询流程
在查询一个Segment中的数据时,根据执行的查询条件,会对首先根据字段加索引的情况对数据进行过滤。然后在进行读取数据,整体的查询流程如下:
-
首先,会按照Segment的行数构建一个row_bitmap,表示记录那些数据需要进行读取,没有使用任何索引的情况下,需要读取所有数据。
-
当查询条件中按前缀索引规则使用到了key时,会先进行ShortKey Index的过滤,可以在ShortKey Index中匹配到的ordinal行号范围,合入到row_bitmap中。
-
当查询条件中列字段存在BitMap Index索引时,会按照BitMap索引直接查出符合条件的ordinal行号,与row_bitmap求交过滤。这里的过滤是精确的,之后去掉该查询条件,这个字段就不会再进行后面索引的过滤。
-
当查询条件中列字段存在BloomFilter索引并且条件为等值(eq,in,is)时,会按BloomFilter索引过滤,这里会走完所有索引,过滤每一个Page的BloomFilter,找出查询条件能命中的所有Page。将索引信息中的ordinal行号范围与row_bitmap求交过滤。
-
当查询条件中列字段存在ZoneMap索引时,会按ZoneMap索引过滤,这里同样会走完所有索引,找出查询条件能与ZoneMap有交集的所有Page。将索引信息中的ordinal行号范围与row_bitmap求交过滤。
-
生成好row_bitmap之后,批量通过每个Column的OrdinalIndex找到到具体的Data Page。
-
批量读取每一列的Column Data Page的数据。在读取时,对于有null值的page,根据null值位图判断当前行是否是null,如果为null进行直接填充即可。
12 总结
Doris目前采用了完全的列存储结构,并提供了丰富的索引应对不同查询场景,为Doris高效的写入、查询性能奠定了夯实的基础。Doris存储层设计灵活,未来还可以进一步增加新的索引、强化数据删除等功能。
原文链接:https://mp.weixin.qq.com/s/aJ3FwDI6KprYYUwXzhl_-A
存储层设计介绍2——写入流程、删除流程分析
1 整体介绍
Doris是基于MPP架构的交互式SQL数据仓库,主要用于解决了近实时的报表和多维分析。Doris高效的导入、查询离不开其存储结构精巧的设计。
本文主要通过阅读Doris BE模块代码,详细分析了Doris BE模块存储层的实现原理,阐述和解密Doris高效的写入、查询能力背后的核心技术。其中包括Doris列存的设计、索引设计、数据读写流程、Compaction流程等功能。这里会通过三篇文章来逐步进行介绍,分别为 《Doris存储层设计介绍1——存储结构设计解析》 、《Doris存储层设计介绍2——写入流程、删除流程分析》、《Doris存储层设计介绍3——读取流程、Compaction流程分析》。
本文为第二篇《Doris存储层设计介绍2——写入流程、删除流程分析》 ,文章详细介绍了数据写入过程中Doris系统内部实现流程,以及Doris对数据按条件删除和按key批量删除的实现流程。
2 名称解释
-
FE: Frontend,即 Doris 的前端节点。主要负责接收和返回客户端请求、元数据以及集群管理、查询计划生成等工作。
-
BE: Backend,即 Doris 的后端节点。主要负责数据存储与管理、查询计划执行等工作。
-
Tablet: Tablet是一张表实际的物理存储单元,一张表按照分区和分桶后在BE构成分布式存储层中以Tablet为单位进行存储,每个Tablet包括元信息及若干个连续的RowSet。
-
Rowset: Rowset是Tablet中一次数据变更的数据集合,数据变更包括了数据导入、删除、更新等。Rowset按版本信息进行记录。每次变更会生成一个版本。
-
Version: 由Start、End两个属性构成,维护数据变更的记录信息。通常用来表示Rowset的版本范围,在一次新导入后生成一个Start,End相等的Rowset,在Compaction后生成一个带范围的Rowset版本。
-
Segment: 表示Rowset中的数据分段。多个Segment构成一个Rowset。
-
Compaction: 连续版本的Rowset合并的过程称为Compaction,合并过程中会对数据进行压缩操作。
3 写入流程
Doris针对不同场景支持了多种形式的数据写入方式,其中包括了从其他存储源导入Broker Load、http同步数据导入Stream Load、例行的Routine Load导入和Insert Into写入等。同时导入流程会涉及FE模块(主要负责导入规划生成和导入任务的调度工作)、BE模块(主要负责数据的 ETL 和存储)、Broker模块(提供 Doris 读取远端存储系统中文件的能力)。其中Broker模块仅在Broker Load类型的导入中应用。
下面以Stream Load写入为例子,描述了Doris的整体的数据写入流程如下图所示:
流程描述如下:
- FE接收用户的写入请求,并随机选出BE作为Coordinator BE。将用户的请求重定向到这个BE上。
- Coordinator BE负责接收用户的数据写入请求,同时请求FE生成执行计划并对调度、管理导入任务LoadJob和导入事务。
- Coordinator BE调度执行导入计划,执行对数据校验、清理之后。
- 数据写入到BE的存储层中。在这个过程中会先写入到内存中,写满一定数据后按照存储层的数据格式写入到物理磁盘上。
本文主要介绍数据写入到BE存储层的详细流程。其余流程不在详细描述。
3.1 数据分发流程
数据在经过清洗过滤后,会通过Open/AddBatch请求分批量的将数据发送给存储层的BE节点上。在一个BE上支持多个LoadJob任务同时并发写入执行。LoadChannelMgr负责管理了这些任务,并对数据进行分发。
数据分发和写入过程如下图所示:
- 每次导入任务LoadJob会建立一个LoadChannel来执行,LoadChannel维护了一次导入的通道,LoadChannel可以将数据分批量写入操作直到导入完成。
- LoadChannel会创建一个TabletsChannel执行具体的导入操作。一个TabletsChannel对应多个Tablet。一次数据批量写入操作中,TabletsChannel将数据分发给对应Tablet,由DeltaWriter将数据写入到Tablet,便开始了真正的写入操作。
3.2 DeltaWriter 与 Memtable
DeltaWriter主要负责不断接收新写入的批量数据,完成单个Tablet的数据写入。由于新增的数据可以是增量Delta部分,因此叫做DeltaWriter。
DeltaWriter数据写入采用了类LSM树的结构,将数据先写到Memtable中,当Memtable数据写满后,会异步flush生成一个Segment进行持久化,同时生成一个新的Memtable继续接收新增数据导入,这个flush操作由MemtableFlushExecutor执行器完成。
Memtable中采用了跳表的结构对数据进行排序,排序规则使用了按照schema的key的顺序依次对字段进行比较。这样保证了写入的每一个写入Segment中的数据是有序的。如果当前模型为非DUP模型(AGG模型和UNIQUE模型)时,还会对相同key的数据进行聚合。
3.3 物理写入
3.3.1 RowsetWriter 各个模块设计
在物理存储层面的写入,由RowsetWriter完成。RowsetWriter中又分为SegmentWriter、ColumnWriter、PageBuilder、IndexBuilder等子模块。
- 其中RowsetWriter从整体上完成一次导入LoadJob任务的写入,一次导入LoadJob任务会生成一个Rowset,一个Rowset表示一次导入成功生效的数据版本。实现上由RowsetWriter负责完成Rowset的写入。
- SegmentWriter负责实现Segment的写入。一个Rowset可以由多个Segment文件组成。
- ColumnWriter被包含在SegmentWriter中,Segment的文件是完全的列存储结构,Segment中包含了各个列和相关的索引数据,每个列的写入由ColumnWriter负责写入。
- 在文件存储格式中,数据和索引都是按Page进行组织,ColumnWriter中又包含了生成数据Page的PageBuilder和生成索引Page的IndexBuilder来完成Page的写入。
- 最后,FileWritableBlock来负责具体的文件的读写。文件的存储格式可以参见《Doris存储层设计介绍1——存储结构设计解析》文档。
3.3.2 RowsetWriter 写入流程
整体的物理写入的如下图所示:
物理写入流程的详细描述:
- 当一个Memtable写满时(默认为100M),将Memtable的数据会flush到磁盘上,这时Memtable内的数据是按key有序的。然后逐行写入到RowsetWriter中。
- RowsetWriter将数据同样逐行写入到SegmentWriter中,RowsetWriter会维护当前正在写入的SegmentWriter以及要写入的文件块列表。每完成写入一个Segment会增加一个文件块对应。
- SegmentWriter将数据按行写入到各个ColumnWriter的中,同时写入ShortKeyIndexBuilder。ShortKeyIndexBuilder主要负责生成ShortKeyIndex的索引Page页。具体的ShortKeyIndex索引格式可以参见《Doris存储层设计介绍1——存储结构设计解析》文档。
- ColumnWriter将数据分别写入PageBuilder和各个IndexBuilder,PageBuilder用来生成ColumnData数据的PageBuilder,各个IndexBuilder包括了(OrdinalIndexBuilder生成OrdinalIndex行号稀疏索引的Page格式、ZoneMapIndexBuilder生成ZoneMapIndex索引的Page格式、BitMapIndexBuilder生成BitMapIndex索引的Page格式、BloomFilterIndexBuilder生成BloomFilterIndex索引的Page格式)。具体参考Doris存储文件格式解析。
- 添加完数据后,RowsetWriter执行flush操作。
- SegmentWriter的flush操作,将数据和索引写入到磁盘。其中对磁盘的读写由FileWritableBlock完成。
- ColumnWriter将各自数据、索引生成的Page顺序写入到文件中。
- SegmentWriter生成SegmentFooter信息,SegmentFooter记录了Segment文件的原数据信息。完成写入操作后,RowsetWriter会再开启新的SegmentWriter,将下一个Memtable写入新的Segment,直到导入完成。
3.4 Rowset 发布
在数据导入完成时,DeltaWriter会将新生成的Rowset进行发布。发布即将这个版本的Rowset设置为可见状态,表示导入数据已经生效能够被查询。而版本信息表示Rowset生效的次序,一次导入会生成一个Rowset,每次导入成功会按序增加版本。整个发布过程如下:
- DeltaWriter统计当前RowsetMeta元数据信息,包括行数、字节数、时间、Segment数量。
- 保存到RowsetMeta中,向FE提交导入事务。当前导入事务由FE开启,用来保证一次导入在各个BE节点的数据的同时生效。
- 在FE协调好之后,由FE统一下发Publish任务使导入的Rowset版本生效。任务中指定了发布的生效version版本信息。之后BE存储层才会将这个版本的Rowset设置为可见。
- Rowset加入到BE存储层的Tablet进行管理。
4 删除流程
目前Delete有两种实现,一种普通的删除类型为DELETE,一种为LOAD_DELETE。
4.1 DELETE 执行流程
DELETE的支持一般的删除操作,实现较为简单,DELETE模式下没有对数据进行实际删除操作,而是对数据删除条件进行了记录。存储在Meta信息中。当执行Base Compaction时删除条件会一起被合入到Base版本中。Base版本为Tablet从[0-x]的第一个Rowset数据版本。具体流程如下:
- 删除时由FE直接下发删除命令和删除条件。
- BE在本地启动一个EngineBatchLoadTask任务,生成新版本的Rowset,并记录删除条件信息。这个删除记录的Rowset与写入过程的略有不同,该Rowset仅记录了删除条件信息,没有实际的数据。
- FE同样发布生效版本。其中会将Rowset加入到Tablet中,保存TabletMeta信息。
4.2 LOAD_DELETE 执行流程
LOAD_DELETE支持了在UNIQUE KEY模型下,实现了通过批量导入要删除的key对数据进行删除,能够支持大量数据删除能力。整体思路是在数据记录中加入删除状态标识,在Compaction流程中会对删除的key进行压缩。Compaction主要负责将多个Rowset版本进行合并,Compaction流程会在后续的文章中进行详细介绍。
目前LOAD_DELETE功能正在研发中,近期的Doris版本会进行发布。
5 总结
本文详细介绍了Doris系统底层存储层的写入流程、删除流程。首先对Doris整体的写入流程进行了描述,然后详细分析了Doris的类LSM存储结构的设计、内存部分数据分发和物理写入流程、Rowset版本发布生效等流程,最后介绍了Doris支持的两种数据删除方式。下一篇会介绍《Doris存储层设计介绍3——读取流程、Compaction流程分析》。
原文链接:
存储层设计介绍1——存储结构设计解析:
https://mp.weixin.qq.com/s/aJ3FwDI6KprYYUwXzhl_-A
存储层设计介绍2——写入流程、删除流程分析:
https://mp.weixin.qq.com/s/xl4ePcsSVPPNQDGBw-KoKA