Parquet与ORC:高性能列式存储

439 阅读6分钟

这是我参与「第四届青训营 」笔记创作活动的的第13天

  • 引言
    • 所有的大数据作业简单来说都可以简化
      • 从存储服务读取数据
      • 计算引擎解析和计算数据
      • 结果呈现
    • "如何高效从存储读取所需的数据"是决定大数据计算作业性能的关键因素
  • 列存 vs. 行存
    • 数据格式层概述
      • 计算层
        • 各种计算引擎
        • Rows+Columns
      • 存储层
        • 承载数据的持久化存储
        • File,Blocks
      • 数据格式层
        • 定义了存储层文件内部的组织格式,计算引擎通过格式层的支持来读写文件
        • File内部的数据布局(Layout+Schema)
    • 数据查询分析场景
      • OLTP vs. OLAP
        • OLTP
          • 在线业务系统
            • 订单、交易、社交、评论
          • 业务特征
            • 事务
            • 实时
            • 低延时
            • 高并发
            • 高可用
          • 数据模型特征
            • schema相对简单
            • 数据维度不多
            • 数据规模较小
        • OLAP
          • 数据仓库或者大数据分析系统
            • 决策分析、BI系统、推荐系统
          • 业务特征
            • 弱事务性
            • 近实时、离线分析
            • 大吞吐
            • 并发相对不高
            • 可用性可以有一定妥协
          • 数据模型特征
            • schema复杂
            • 数据维度很多
            • 数据规模巨大
    • OLTP——行存
      • 每行的数据在文件上是连续存储的
      • 读取整行数据效率高,单次IO顺序读即可
      • 典型系统
        • 关系型数据库
        • key-value数据库
    • OLAP——列存
      • 每列的数据在文件上是连续存储的
      • 读取整列的效率较高
      • 同列的数据类型一致,压缩编码的效率更好
      • 典型系统
        • 大数据分析系统
          • SQL-on-Hadoop,数据湖分析
        • 数据仓库
          • ClickHouse,Greenplum,阿里云MaxCompute
    • 总结
      • 格式层定义了数据的布局,连接计算引擎和存储服务
      • OLTPOLAP场景差异明显
      • 业务场景决定了技术实现,行存适用于OLTP,列存适用于OLAP
  • Parquet原理详解
    • 简介
      • 大数据分析领域使用最广的列存格式
      • Spark推荐存储格式
    • 数据模型
      • Dremel
        • Protocol Buffer定义
        • 支持可选和重复字段
        • 支持嵌套类型
          • 嵌套类型只保存叶子节点数据
        • repetition Level
          • 该字段在field path上第几个重复字段上出现
          • 0:标识新的record
        • Definition Level
          • 用来记录field path中,有多少个字段是可以不存在(optional/repeated)而实际出现的
        • Re-Assembly
          • 根据全部或者部分列数据,重新构造record
          • 构造FSM状态机
          • 根据同一个column,下一个记录的repetionlevel决定继续读的列
    • 数据布局
      • RowGroup
        • 每一个行组包含一定数量或者固定大小的行的集合
      • ColumnChunk
        • RowGroup中按照列切分成多个ColumnChunk
      • Page
        • ColumnChunk内部继续切分为Page,一般建议8KB大小。压缩和编码的基本单元
          • 根据保存的数据类型分为Data Page、Dictionary Page、Index Page
      • Footer保存文件元信息
        • Schema
        • Config
        • Metadata
          • Rowgroup Meta
            • Column Meta
    • 编码Encoding
      • Plain直接存储原始数据
      • Run Length Encoding(RLE)
        • 适用于列基数不大,重复值较多的场景,例如:枚举、Boolean、固定的选项等
          • Bit-Pack Encoding
            • 配合RLE编码使用,让整形数字存储的更加紧凑
      • 字典编码Dictionary Encoding
        • 多用于编码字符串
        • 适用于列基数不大的场景,构建字典表,写入到Dictionary Page;把数据用字典index替换,然后用RLE编码
      • 默认场景下parquet-mr会自动根据数据特征选择
      • 业务自定义org.apache.parquet.column.values.factory.ValuesWriteFactory
    • 压缩compression
      • page完成encoding以后,进行压缩
      • 支持多种压缩算法
      • snappy
        • 压缩速度块,压缩比不高,适用于热数据
      • gzip
        • 压缩速度慢,压缩比高,适用于冷数据
      • zstd
        • 新引入的压缩算法,压缩比和gzip差不多,而且压缩速度比肩snappy
      • 建议选择snappyzstd,根据业务数据类型充分测试压缩效果,以及对查询性能的影响
    • 索引Index
      • 和传统给的数据库相比,索引支持非常简陋
      • Min-Max Index
        • 记录page内部columnmin_valuemax_value
      • Column Index
        • footer里的column metadata包含columnChunk的全部pageMin-Max value
      • Offset Index
        • 记录page在文件中的offsetpagerow range
      • bloom filter
        • parquet.bloom.filter.enabled
        • 对于列基数比较大的场景,或者非排序列的过滤,Min-Max Index很难发挥作用
        • 引入bloom filter加速过滤匹配判定
        • 每个ColumnChunk的头部保存的bloom filter数据
        • footer记录bloom filterpage offset
    • 排序Ordering
      • 类似于聚集索引的概念
      • 排序帮助更好的过滤掉无关的RowGroup或者Page
        • 对于少量数据seek很有帮助
      • parquet Format支持sortingColumns
      • parquet Library目前没有支持
      • 依赖业务侧根据查询特征去保证顺序
    • 过滤下推Predicate PushDown
      • parquet mr库实现,实现高效的过滤机制
      • 引擎侧传入filter expression
      • parquet mr转换为具体column的条件匹配
      • 查询footer里的column index,定位到具体的行号
      • 返回有效的数据给引擎侧
    • spark集成
      • 向量化读
        • parquetFileFormat
        • 向量化读开关
          • spark.sql.parquet.ebableVectorizeReader
        • 向量化读是主流大数据分析引擎的标准实践,可以极大的提高查询性能
        • sparkbatch的方式从parquet读取数据,下推的逻辑也会适配batch的方式
  • ORC详解与对比
    • 大数据分析领域使用最广的列存格式之一,出自于hive项目
    • 数据模型
      • ORC会给包括根节点在内的中间节点都创建一个column
      • 嵌套类型或者集合类型支持和parquet差别较大
      • optional和repeated字段依赖父节点记录额外信息来重新assembly数据
    • 数据布局
      • 类似parquet
      • *rooter+stripe+column+page(row group)*结构
      • encoding/compression/index支持上和parquet几乎一致
    • ACID特性简介
      • 支持Hive Transactions实现,目前只有hive本身集成
      • 类似delta lake/hudi/iceberg
      • 基于Base+Delta+Compaction的设计
    • AliORC
      • ORC在阿里云计算平台被广泛应用,主流产品maxcompute+交互式分析hologres的最新版本都支持ORC格式
      • AliORC是对ORC的深度定制版
      • 索引增强
        • 支持clusterd index,更快的主键查找
        • 支持bitmap index,更快的过滤
          • roaring bitmap
      • 小列聚合
        • 小列聚合,减少小IO
          • 重排chunk
      • 异步预取
        • 异步预取数据
        • 计算逻辑和数据读取并行化
    • parquet vs. ORC
      • 从原理层面,最大的差别就是对于nestedType和复杂类型的处理上
      • parquet的算法上要复杂很多,带来的cpu的开销比orc略大
      • orc的算法相对简单,但是要读取更多数据
      • 因此,这个差异对业务效果的影响,取决于实际的业务场景
  • 列存演进
    • 数仓中的列存
      • clickhousemergeTree引擎也是基于列存构建的
      • 默认情况下列按照column拆分
      • 支持更加丰富的索引
      • 湖仓一体的大趋势
    • 存储侧下推
      • 更多的下推工作下沉到存储服务侧
      • 越接近数据,下推过滤的效率越高
      • 例如AWS S3 Select功能
      • 挑战
        • 存储侧感知schema
        • 计算生态的兼容和集成
    • column family支持
      • 背景
        • hudi数据湖场景下,支持部分列的快速更新
        • parquet格式里引入column family概念,把需要更新的列拆成独立的column family
        • 深度改造hudiupdatequery逻辑,根据column family选择覆盖对应的column family
        • update操作实际效果有10+倍的提升