这是我参与「第四届青训营 」笔记创作活动的的第13天
- 引言
- 所有的大数据作业简单来说都可以简化
- 从存储服务读取数据
- 计算引擎解析和计算数据
- 结果呈现
- "如何高效从存储读取所需的数据"是决定大数据计算作业性能的关键因素
- 所有的大数据作业简单来说都可以简化
- 列存 vs. 行存
- 数据格式层概述
- 计算层
- 各种计算引擎
- Rows+Columns
- 存储层
- 承载数据的持久化存储
- File,Blocks
- 数据格式层
- 定义了存储层文件内部的组织格式,计算引擎通过格式层的支持来读写文件
- File内部的数据布局(Layout+Schema)
- 计算层
- 数据查询分析场景
- OLTP vs. OLAP
- OLTP
- 在线业务系统
- 订单、交易、社交、评论
- 业务特征
- 事务
- 实时
- 低延时
- 高并发
- 高可用
- 数据模型特征
- schema相对简单
- 数据维度不多
- 数据规模较小
- 在线业务系统
- OLAP
- 数据仓库或者大数据分析系统
- 决策分析、BI系统、推荐系统
- 业务特征
- 弱事务性
- 近实时、离线分析
- 大吞吐
- 并发相对不高
- 可用性可以有一定妥协
- 数据模型特征
- schema复杂
- 数据维度很多
- 数据规模巨大
- 数据仓库或者大数据分析系统
- OLTP
- OLTP vs. OLAP
- OLTP——行存
- 每行的数据在文件上是连续存储的
- 读取整行数据效率高,单次IO顺序读即可
- 典型系统
- 关系型数据库
- key-value数据库
- OLAP——列存
- 每列的数据在文件上是连续存储的
- 读取整列的效率较高
- 同列的数据类型一致,压缩编码的效率更好
- 典型系统
- 大数据分析系统
- SQL-on-Hadoop,数据湖分析
- 数据仓库
- ClickHouse,Greenplum,阿里云MaxCompute
- 大数据分析系统
- 总结
- 格式层定义了数据的布局,连接计算引擎和存储服务
- OLTP和OLAP场景差异明显
- 业务场景决定了技术实现,行存适用于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决定继续读的列
- Dremel
- 数据布局
- RowGroup
- 每一个行组包含一定数量或者固定大小的行的集合
- ColumnChunk
- RowGroup中按照列切分成多个ColumnChunk
- Page
- ColumnChunk内部继续切分为Page,一般建议8KB大小。压缩和编码的基本单元
- 根据保存的数据类型分为Data Page、Dictionary Page、Index Page
- ColumnChunk内部继续切分为Page,一般建议8KB大小。压缩和编码的基本单元
- Footer保存文件元信息
- Schema
- Config
- Metadata
- Rowgroup Meta
- Column Meta
- Rowgroup Meta
- RowGroup
- 编码Encoding
- Plain直接存储原始数据
- Run Length Encoding(RLE)
- 适用于列基数不大,重复值较多的场景,例如:枚举、Boolean、固定的选项等
- Bit-Pack Encoding
- 配合RLE编码使用,让整形数字存储的更加紧凑
- Bit-Pack Encoding
- 适用于列基数不大,重复值较多的场景,例如:枚举、Boolean、固定的选项等
- 字典编码Dictionary Encoding
- 多用于编码字符串
- 适用于列基数不大的场景,构建字典表,写入到Dictionary Page;把数据用字典index替换,然后用RLE编码
- 默认场景下parquet-mr会自动根据数据特征选择
- 业务自定义
org.apache.parquet.column.values.factory.ValuesWriteFactory
- 压缩compression
- page完成encoding以后,进行压缩
- 支持多种压缩算法
- snappy
- 压缩速度块,压缩比不高,适用于热数据
- gzip
- 压缩速度慢,压缩比高,适用于冷数据
- zstd
- 新引入的压缩算法,压缩比和gzip差不多,而且压缩速度比肩snappy
- 建议选择snappy或zstd,根据业务数据类型充分测试压缩效果,以及对查询性能的影响
- 索引Index
- 和传统给的数据库相比,索引支持非常简陋
- Min-Max Index
- 记录page内部column的min_value和max_value
- Column Index
- footer里的column metadata包含columnChunk的全部page的Min-Max value
- Offset Index
- 记录page在文件中的offset和page的row range
- bloom filter
parquet.bloom.filter.enabled- 对于列基数比较大的场景,或者非排序列的过滤,Min-Max Index很难发挥作用
- 引入bloom filter加速过滤匹配判定
- 每个ColumnChunk的头部保存的bloom filter数据
- footer记录bloom filter的page 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
- 向量化读是主流大数据分析引擎的标准实践,可以极大的提高查询性能
- spark以batch的方式从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
- 小列聚合,减少小IO
- 异步预取
- 异步预取数据
- 计算逻辑和数据读取并行化
- parquet vs. ORC
- 从原理层面,最大的差别就是对于nestedType和复杂类型的处理上
- parquet的算法上要复杂很多,带来的cpu的开销比orc略大
- orc的算法相对简单,但是要读取更多数据
- 因此,这个差异对业务效果的影响,取决于实际的业务场景
- 列存演进
- 数仓中的列存
- clickhouse的mergeTree引擎也是基于列存构建的
- 默认情况下列按照column拆分
- 支持更加丰富的索引
- 湖仓一体的大趋势
- 存储侧下推
- 更多的下推工作下沉到存储服务侧
- 越接近数据,下推过滤的效率越高
- 例如AWS S3 Select功能
- 挑战
- 存储侧感知schema
- 计算生态的兼容和集成
- column family支持
- 背景
- hudi数据湖场景下,支持部分列的快速更新
- 在parquet格式里引入column family概念,把需要更新的列拆成独立的column family
- 深度改造hudi的update和query逻辑,根据column family选择覆盖对应的column family
- update操作实际效果有10+倍的提升
- 背景
- 数仓中的列存