文章

ORC 教程 | ORC 概述

背景

多年来 Hive 的 RCFile 是Hadoop用于存储表格数据的标准格式。 但是 RCFile 有局限性,因为它将每一列视为没有语义的二进制Blob。 在Hive 0.11中,我们添加了一个名为优化行列式文件(ORC)的新文件格式,该文件使用并保留表定义中的类型信息。

ORC使用特定类型的读取器和写入器,提供轻量级压缩技术,如字典编码位填充增量编码RLE编码,从而导致文件大小显著减小。 此外,ORC可以在轻量级压缩的基础上应用通用压缩,使用zlibSnappy,以进一步减小文件大小。

然而,存储节省仅仅是收益的一部分。ORC支持投影,可选择要读取的列的子集,以便仅读取查询所需的字节,因此仅读取一个列的查询只读取所需的字节。

此外,ORC文件包括轻量级索引,其中包括每列在每组10,000行和整个文件中的最小和最大值。

使用Hive的谓词下推过滤器,文件读取器可以跳过对于此查询而言不重要的整组行。

image.png

FileTail

由于HDFS不支持在写入文件后更改文件中的数据,因此ORC将顶层索引存储在文件末尾。

文件的整体结构如下所示。 文件的尾部包括三个部分:文件元数据(Metadata)文件页脚(Footer)尾部附录(Postscript)

ORC的元数据使用Protocol Buffers存储,这提供了在不破坏读取器的情况下添加新字段的能力。

image.png

Postscript

Postscript部分提供了解释文件其余部分所需的信息: 包括FooterMetadata的长度、文件的版本以及使用的通用压缩类型(例如,无压缩zlibsnappy)。

postscript永远不会被压缩,并在文件结束前一个字节结束(最后一个字节存储 postscript的长度)。

postscript 中存储的版本是保证能够读取文件的最低版本的Hive,以主版本号和次版本号的序列存储。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
message PostScript {
  optional uint64 footerLength = 1;                 // 记录当前的footer的长度
  optional CompressionKind compression = 2;         // 记录当前压缩信息
  optional uint64 compressionBlockSize = 3;

  // postscript 中存储的版本是保证能够读取文件的最低版本的Hive,以主版本号和次版本号的序列存储。
  //   [0, 11] = Hive 0.11
  //   [0, 12] = Hive 0.12
  repeated uint32 version = 4 [packed = true];

  optional uint64 metadataLength = 5;              // 记录metadata的长度

  // 写入文件的写入器版本。
  // 当我们对写入器进行修复或进行较大更改时,此数字将更新,以便读取器可以检测数据中是否存在特定的错误。
  //
  // ORC Java写入器的版本:
  //   0 = original
  //   1 = HIVE-8732 fixed (fixed stripe/file maximum statistics & string statistics use utf8 for min/max)
  //   2 = HIVE-4243 fixed (use real column names from Hive tables)
  //   3 = HIVE-12055 added (vectorized writer implementation)
  //   4 = HIVE-13083 fixed (decimals write present stream correctly)
  //   5 = ORC-101 fixed (bloom filters use utf8 consistently)
  //   6 = ORC-135 fixed (timestamp statistics use utc)
  //   7 = ORC-517 fixed (decimal64 min/max incorrect)
  //   8 = ORC-203 added (trim very long string statistics)
  //   9 = ORC-14 added (column encryption)
  //
  // ORC C++ 写入器的版本 :
  //   6 = original
  //
  // ORC Presto 写入器的版本 :
  //   6 = original
  //
  // ORC Go 写入器的版本 :
  //   6 = original
  //
  // ORC Trino 写入器的版本 :
  //   6 = original
  optional uint32 writerVersion = 6;
  
  optional uint64 stripeStatisticsLength = 7;  // 加密stripe统计信息的字节数
  optional string magic = 8000;                 // 魔数
}

Footer 部分包含文件主体的布局类型schema信息行数以及每列的统计信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
message Footer {
  optional uint64 headerLength = 1;           // orc 文件header长度 -- OrcFile.MAGIC 魔数
  optional uint64 contentLength = 2;          // orc 文件body长度
  repeated StripeInformation stripes = 3;     // stripe 统计信息
  repeated Type types = 4;                    // schema 信息
  repeated UserMetadataItem metadata = 5;     // 用户自定义元信息
  optional uint64 numberOfRows = 6;           // 包含的行数据
  repeated ColumnStatistics statistics = 7;   // 文件级别的每个列的统计信息statics
  optional uint32 rowIndexStride = 8;         // 每个索引条目中的最大行数。

  // 每个编写 ORC 文件的实现都应该注册一个代码。
  // 0 = ORC Java
  // 1 = ORC C++
  // 2 = Presto
  // 3 = Scritchley Go from https://github.com/scritchley/orc
  // 4 = Trino
  optional uint32 writer = 9;

  // information about the encryption in this file
  optional Encryption encryption = 10;       // 对于加密列的处理
  optional CalendarKind calendar = 11;

  // 有关编写文件的软件版本的信息性描述。假定它在给定的写入器内,例如 ORC 1.7.2 = "1.7.2"。它可以包括后缀,如“-SNAPSHOT”。
  optional string softwareVersion = 12;
}

Metadata

Metadata 部分主要用于记录每个stripe 的每个列的统计信息

1
2
3
4
5
6
7
message Metadata { 
  repeated StripeStatistics stripeStats = 1;
}
// StripeStatistics(每个stripe一个),其中每个包含每列的 ColumnStatistics.
message StripeStatistics {
  repeated ColumnStatistics colStats = 1;   // 每个列的统计统计信息
}

Stripe

image.png

StripeFooter

Stripe Footer 包含每列的编码以及包括它们的位置在内的流目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
message StripeFooter {
  repeated Stream streams = 1;                       // 每个流的位置(未加密流)。
  repeated ColumnEncoding columns = 2;               // 每个列的编码类型
  optional string writerTimezone = 3;                // 写时区
  repeated StripeEncryptionVariant encryption = 4;   //加密列流类型统计
}

message ColumnEncoding {
  enum Kind {
    DIRECT = 0;        // 直接编码
    DICTIONARY = 1;    // 字典编码
    DIRECT_V2 = 2;
    DICTIONARY_V2 = 3;
  }
  optional Kind kind = 1;
  optional uint32 dictionarySize = 2;

  // 此列的BloomFile 编码:
  //   0 or missing = 没有编码
  //   1            = ORC-135 (utc for timestamps)
  optional uint32 bloomEncoding = 3;
}

message Stream {
  // 数据流的类型
  enum Kind {
    PRESENT = 0;                  // 空值位图流
    DATA = 1;                     // 数据实体
    LENGTH = 2;                   // 
    DICTIONARY_DATA = 3;          // 
    DICTIONARY_COUNT = 4;         // 
    SECONDARY = 5;                // 
    ROW_INDEX = 6;                // 原始索引类型
    BLOOM_FILTER = 7;             // 列的BloomFilter数据体
    BLOOM_FILTER_UTF8 = 8;        // 列的BloomFilter数据体
    ENCRYPTED_INDEX = 9;          // 加密索引类型
    ENCRYPTED_DATA = 10;          // 加密列数据类型
    STRIPE_STATISTICS = 100;      // Stripe 统计信息数据体
    FILE_STATISTICS = 101;        // 文件统计信息数据体
  }
  optional Kind kind = 1;          // 表示此数据流的类型
  optional uint32 column = 2;      // 列的标识ID
  optional uint64 length = 3;      // 流长度
}

Data

SmallInt、Int 和 BigInt 列
ColumnEncoding包含的 DATA Stream是否可选数据内容
DIRECTPRESENT是(数据全非空可没有)布尔 RLE
 DATA有符号整数 RLE v1
DIRECT_V2PRESENT是(数据全非空可没有)布尔 RLE
 DATA有符号整数RLE v2
Float 和 Double 列
ColumnEncoding包含的 DATA Stream是否可选数据内容
DIRECTPRESENT是(数据全非空可没有)布尔 RLE
 DATAIEEE754浮点表示
String、Char 和 VarChar 列

String、char 和 varchar 列可以使用字典编码直接编码直接编码应为当存在许多不同值时首选orc.dictionary.key.threshold。 对于直接编码,UTF-8 字节保存在DATA流中,并且每个值的长度将写入LENGTH流中。直接编码,如果值为[“Nevada”, “California”];数据将是“NevadaCalifornia”,长度将是[6, 10]。 对于字典编码,字典被排序(在字典中 UTF-8 编码中的字节顺序)和 UTF-8 字节的顺序 每个唯一值都放入DICTIONARY_DATA中。每个的长度字典中的项被放入LENGTH流中。 DATA流由对字典元素的引用序列组成。 在字典编码中,如果值为 [“Nevada”, “加利福尼亚州”、“内华达州”、“加利福尼亚州”和“佛罗里达州”];这 DICTIONARY_DATA将是“CaliforniaFloridaNevada”,而LENGTH将是[10, 7, 6]。DATA将为[2, 0, 2, 0,1]

ColumnEncoding包含的 DATA Stream是否可选数据内容
DIRECTPRESENT是(数据全非空可没有)布尔 RLE
 DATA字符串内容
 LENGTH无符号整数 RLE v1
DICTIONARYPRESENT是(数据全非空可没有)布尔 RLE
 DATA无符号整数 RLE v1
 DICTIONARY_DATA字符串内容
 LENGTH无符号整数 RLE v1
DIRECT_V2PRESENT是(数据全非空可没有)布尔 RLE
 DATA字符串内容
 LENGTH无符号整数 RLE v2
DICTIONARY_V2PRESENT是(数据全非空可没有)布尔 RLE
 DATA无符号整数 RLE v2
 DICTIONARY_DATA字符串内容
 LENGTH无符号整数 RLE v2
Bool,TinyInt列
ColumnEncoding包含的 DATA Stream是否可选数据内容
DIRECTPRESENT是(数据全非空可没有)布尔 RLE
 DATA布尔 RLE
Bytes 列
ColumnEncoding包含的 DATA Stream是否可选数据内容
DIRECTPRESENT是(数据全非空可没有)布尔 RLE
 DATA字符串内容
 LENGTH无符号整数 RLE v1
DICTIONARY_V2PRESENT是(数据全非空可没有)布尔 RLE
 DATA无符号整数 RLE v2
 DICTIONARY_DATA字符串内容
 LENGTH无符号整数 RLE v2
Decimal 列
ColumnEncoding包含的 DATA Stream是否可选数据内容
DIRECTPRESENT是(数据全非空可没有)布尔 RLE
 DATA有符号整数 RLE v2
DIRECT_V2PRESENT是(数据全非空可没有)布尔 RLE
 DATA无界底数128变量
 SECONDARY有符号整数 RLE v2
Date 列
ColumnEncoding包含的 DATA Stream是否可选数据内容
DIRECTPRESENT是(数据全非空可没有)布尔 RLE
 DATA有符号整数 RLE v1
DIRECT_V2PRESENT是(数据全非空可没有)布尔 RLE
 DATA有符号整数 RLE v2
Timestamp 列
ColumnEncoding包含的 DATA Stream是否可选数据内容
DIRECTPRESENT是(数据全非空可没有)布尔 RLE
 DATA有符号整数 RLE v1
 SECONDARY无符号整数 RLE v1
DIRECT_V2PRESENT是(数据全非空可没有)布尔 RLE
 DATA有符号整数 RLE v2
 SECONDARY无符号整数 RLE v2

Index

image.png

Row Group Index

row group index 由每个行组的基元列的 ROW_INDEX stream 组成。 行组由写入器控制,默认为(orc.row.index.stride) 10,000 行。每个 RowIndexEntry 给出了该列的每个流的位置和该行组的统计信息。 ROW_INDEX stream 放置在 stream 的前面,因为在默认的流式传输情况下它们不需要被读取。 它们只在使用谓词下推或读取器寻求特定行时加载。

1
2
3
4
5
6
7
8
9
10
// 每个RowGroup的每个列的统计信息
message RowIndexEntry {
  repeated uint64 positions = 1 [packed=true];      // 包含data区当前列所有stream的索引信息
  optional ColumnStatistics statistics = 2;         // 包含行组内的列的相关统计信息,最大值,最小值,求和,
}

// RowGroup 中的每个列的索引信息
message RowIndex {
  repeated RowIndexEntry entry = 1;           
}
Bloom Filter Index

Hive 1.2.0 开始,Bloom Filters 被添加到 ORC 索引中。

谓词下推可以利用布隆过滤器更好地剪枝不满足过滤条件的行组。布隆过滤器索引包括通过 orc.bloom.filter.columns 属性指定的每个列的 BLOOM_FILTER 流。 BLOOM_FILTER 流为列中的每个row group(默认为 10,000 行)记录一个布隆过滤器条目。只有满足 min/max 行索引评估的行组将被用于布隆过滤器索引的评估。

1
2
3
4
5
6
7
8
message BloomFilter {
 optional uint32 numHashFunctions = 1;
 repeated fixed64 bitset = 2;
 optional bytes utf8bitset = 3;
}
message BloomFilterIndex {
 repeated BloomFilter bloomFilter = 1;
}

ORC 配置汇总

orc 配置项hive 配置项默认值说明
orc.stripe.sizehive.exec.orc.default.stripe.size64 * 1024*1024orc stripe 大小(STRIPE_SIZE)
orc.stripe.row.countorc.stripe.row.countInteger.MAX_VALUE每个stripe可以容纳的数量大小
orc.block.sizehive.exec.orc.default.block.size256L * 1024 * 1024orc 文件块大小
orc.create.indexorc.create.indextrue是否创建索引作为文件的一部分
orc.row.index.stridehive.exec.orc.default.row.index.stride10000定义ORC索引的默认步幅,以行数表示。 (步幅是索引条目表示的行数。)
orc.compress.sizehive.exec.orc.default.buffer.size256 * 1024定义ORC缓冲区的默认大小(BUFFER_SIZE),以字节为单位。
orc.base.delta.ratiohive.exec.orc.base.delta.ratio8在STRIPE_SIZE和BUFFER_SIZE方面的基础写入器和增量写入器的比率。这指的是基础写入器和增量写入器在STRIPE_SIZE和BUFFER_SIZE方面的相对比率。
orc.block.paddinghive.exec.orc.default.block.paddingtrue定义是否应将Stripe填充到HDFS块边界。这个设置指定了是否应该在写入ORC文件时将stripe的大小调整为HDFS块的大小。填充stripe可以有助于优化读取性能。
orc.compresshive.exec.orc.default.compressZLIB定义ORC文件的默认压缩编解码器
orc.write.formathive.exec.orc.write.format0.12定义要写入的文件版本。可能的取值为0.11和 0.12
orc.buffer.size.enforcehive.exec.orc.buffer.size.enforcefalse定义强制执行ORC压缩缓冲区大小
orc.encoding.strategyhive.exec.orc.encoding.strategySPEED定义在写入数据时要使用的编码策略。更改此选项仅影响整数的轻量级编码。此标志不会更改更高级别压缩编解码器(如ZLIB)的压缩级别。
orc.compression.strategyhive.exec.orc.compression.strategySPEED定义在写入数据时要使用的压缩策略。这会更改更高级别压缩编解码器(如ZLIB)的压缩级别。
orc.block.padding.tolerancehive.exec.orc.block.padding.tolerance0.05定义块填充的容忍度,以stripe大小的小分数表示(例如,默认值0.05表示stripe大小的5%)。对于默认的64Mb ORC Stripe和256Mb HDFS块,5%的默认块填充容忍度将在256Mb块内最多保留3.2Mb的填充空间。在这种情况下,如果块内的可用空间超过3.2Mb,将插入一个新的较小stripe以适应该空间。这将确保写入的任何stripe都不会在块边界上并导致节点本地任务内的远程读取。
orc.use.zerocopyhive.exec.orc.zerocopyfalse使用零拷贝技术
orc.skip.corrupt.datahive.exec.orc.skip.corrupt.datafalse如果ORC读取器遇到损坏的数据,此值将用于确定是跳过损坏的数据还是引发异常。默认行为是抛出异常
orc.tolerate.missing.schemahive.exec.orc.tolerate.missing.schematrue早于 HIVE-4243 的写入器可能具有不准确的模式元数据。此设置将启用最佳尝试模式演变,而不是拒绝不匹配的模式。
orc.memory.poolhive.exec.orc.memory.pool0.5orc write可以使用的堆的最大百分比
orc.dictionary.key.thresholdhive.exec.orc.dictionary.key.size.threshold0.8字典编码阈值,如果(字典中不同键的数量/非空行)比例超过这个阈值,则关闭字典
orc.dictionary.early.checkhive.orc.row.index.stride.dictionary.checktrue如果启用了字典检查,将在第一个行索引步幅(默认为10000行)之后进行字典检查,否则将在写入第一个stripe之前进行字典检查。在两种情况下,此后将保留使用字典与否的决策。
orc.dictionary.implementationorc.dictionary.implementationrbtree字符串字典编码的具体实现类: rbtree : 在字典的上下文中,红黑树可能用于快速的查找和操作字典中的键值对。hash: 在这个上下文中,提到使用哈希表作为字典的实现方式,表示字典的存储和检索操作是基于哈希表进行的。
orc.bloom.filter.fpporc.default.bloom.fpp0.05布隆过滤器的默认误报概率
orc.bloom.filter.columnsorc.bloom.filter.columns””写入时要为其创建 BloomFile 的列集合
orc.bloom.filter.write.versionorc.bloom.filter.write.versionOrcFile.BloomFilterVersion.UTF8BloomFilter 的版本 : original : 可以确保旧的读取器和新的读取器都能够有效地使用布隆过滤器,而不受版本差异的影响。utf8 : 仅支持新版本的Bloom过滤器
orc.bloom.filter.ignore.non-utf8orc.bloom.filter.ignore.non-utf8false设置reader是否忽略非utf8的bloom过滤器编码类型
orc.max.file.lengthorc.max.file.lengthLong.MAX_VALUE查找文件尾部时要读取的文件的最大大小, 这主要用于流式摄取,以便在文件仍然打开时读取中间页脚
orc.mapred.input.schemanullnull读取时反序列化的schema, 通过 TypeDescription.fromString 来解析这些字符串
orc.mapred.map.output.key.schemanullnull这是关于 MapReduce shuffle key 相关的schema。这里的值是通过 TypeDescription.fromString 进行解释的。
orc.mapred.map.output.value.schemanullnull这是关于 MapReduce shuffle value 相关的schema。这里的值是通过 TypeDescription.fromString 进行解释的。
orc.mapred.output.schemanullnull用户希望写入的schema。这些值是通过 TypeDescription.fromString 进行解释的。
orc.include.columnshive.io.file.readcolumn.idsnull要读取的列的 ID 列表,用逗号分隔,其中 0 表示第一列,1 表示下一列,依此类推。
orc.force.positional.evolution.levelorc.force.positional.evolution.level1要求schema演变匹配通过位置而不是列名定义的级别列数。这提供了与 Hive 2.1 的向后兼容性。
orc.rows.between.memory.checksorc.rows.between.memory.checks5000MemoryManager应多久检查一次内存大小?以添加到所有写入器的行为单位进行测量。有效范围为[110000],主要用于测试。将此设置过低可能会对性能产生负面影响。使用 orc.stripe.row。如果值大于orc.stripe.row.count,则改为计数。
orc.overwrite.output.fileorc.overwrite.output.filefalse如果文件已经存在则是否要覆盖, 默认不覆盖
orc.force.positional.evolutionorc.force.positional.evolutionfalse要求schema演化使用位置而不是列名来匹配顶级列。这提供了与Hive 2.1的向后兼容性
orc.schema.evolution.case.sensitiveorc.schema.evolution.case.sensitivetrueschema 演化过程中是否区分大小写
orc.kryo.sargorc.kryo.sargnullKryo 和 Base64 编码的 SearchArgument 用于谓词下推。
orc.kryo.sarg.buffernull8192用于谓词下推的kryo buffer大小
orc.sarg.column.namesorg.sarg.column.namesnull谓词下推的列名
orc.sarg.to.filterorg.sarg.to.filterfalse用于确定是否允许SArg成为过滤器的布尔标志
orc.filter.use.selectedorc.filter.use.selectedfalse布尔标志,用于确定读取应用程序是否支持向量查询。如果为false,则ORC读取器的输出必须重新应用筛选器,以避免在未选中的行中使用未设置的值
orc.filter.pluginorc.filter.pluginfalse允许在读取期间使用插件筛选器。插件过滤器是通过 org.apache.orc.filter.PluginFilterService 的服务发现,如果存在多个过滤器, 则顺序不确定
orc.write.variable.length.blocksnullfalse关于 ORC 写入器是否应写入可变长度 HDFS 块的布尔标志
orc.column.encoding.directorc.column.encoding.direct””要跳过字典编码的列
orc.max.disk.range.chunk.limithive.exec.orc.max.disk.range.chunk.limitInteger.MAX_VALUE - 1024要读取超过2GB的strip时,最大的限制块大小
orc.min.disk.seek.sizeorc.min.disk.seek.size0在确定连续读取时,此大小内的间隙将连续读取而不查找, 默认值为零将禁用此优化
orc.min.disk.seek.size.toleranceorc.min.disk.seek.size.tolerance0.00定义由于 orc.min.disk.seek.size 读取的额外字节的容差。如果(bytesRead-bytesNeeded)/ BytesReeded大于此阈值,则执行额外的工作以在读取之后从内存中删除额外的字节
orc.encryptorc.encryptnull要加密的密钥和列的列表
orc.maskorc.masknull要应用于加密列的掩码
orc.key.providerorc.key.providerhadoop用于加密的密钥提供程序的类型
orc.proleptic.gregorianorc.proleptic.gregorianfalse在读取和写入日期和时间时,我们是否应该使用推进格里高利历而不是混合儒略格里高利历?在 Hive 3.1 之前和 Spark 3.0 之前,它们使用了混合历法。
orc.proleptic.gregorian.defaultorc.proleptic.gregorian.defaultfalse这个值控制着在 ORC 27 版本之前的文件中使用混合历法还是推进历法。只有 Hive 3.1 和 C++ 库在写入时使用了推进历法,所以混合历法是默认值。
orc.row.batch.sizeorc.row.batch.size1024ORC vector reader batch 中包含的行数 应仔细选择该值,以最小化开销并避免读取数据时出现OOM
orc.row.child.limitorc.row.child.limit1024 * 32ORC行写入器将批写入文件之前要缓冲的最大子元素数
本文由作者按照 CC BY 4.0 进行授权

© . 保留部分权利。

本站采用 Jekyll 主题 Chirpy