物化列:字节为解决 Spark 嵌套列查询性能低下的优化
Posted 过往记忆大数据
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了物化列:字节为解决 Spark 嵌套列查询性能低下的优化相关的知识,希望对你有一定的参考价值。
本文来自11月举办的 Data + AI Summit 2020 (原 Spark+AI Summit),主题为《Materialized Column- An Efficient Way to Optimize Queries on Nested Columns》的分享,作者为字节跳动的郭俊。
在数据仓库领域,使用复杂类型(如map)中的一列或多列,或者将许多子字段放入其中的场景是非常常见的。这些操作可能会极大地影响查询性能,因为:
•这些操作非常废 IO。比如我们有一个字段类型为 Map,其包含数十个子字段那么在读取这列时需要把整个列都读出来。并且 Spark 将遍历整个 map 获得目标键的值。•在读取嵌套类型列时不能利用向量化读取。•读取嵌套列时不能使用算子下推(Filter pushdown)。
在过去的一年里,字节的数据引擎团队在 Apache Spark 中添加了一系列优化来解决 Parquet 格式中的上述问题。
其中包括支持对 Parquet 中复杂数据类型的向量化读取,允许对 Parquet 中的结构列进行子字段修剪,等等。此外,该团队还开发了一个名为物化列(materialized column)的新特性,透明地解决了任意列式存储(不仅仅是Parquet)的上述问题。在过去的一年里,物化列在字节跳动数据仓库中工作得很好。以一个典型的表为例,每天增量的数据量约为 200 TB。在它上面创建 15 个物化列可以提高超过110%的查询性能,而存储开销不到7%。在这次讨论中,我们将深入介绍 Spark SQL 中物化列的内部原理,描述物化列的使用场景。
今天的主题主要分为:
•简单介绍字节跳动的 Spark SQL 使用情况•为什么嵌套类型被广泛使用•嵌套数据类型的主要问题•优化解决方案•物化列是如何解决这些问题的
字节跳动的 Spark SQL 使用情况
下面是 Spark SQL 在字节跳动的应用。
2016年主要是小规模的测试阶段
2017年用于处理 Ad-hoc 工作负载
2018年在生产环境下处理少量的 ETL 管道工作;
2019年在生产环境下全面部署;
2020年成为 DW 领域的主要计算引擎。
为什么嵌套类型被广泛使用
字节跳动主要有两种类型的数据。第一类也是最大的一类也就是事件日志(Event log),通过事件日志可以进行一些事件跟踪。在字节每天都有大量的事件日志,而且不同的事件日志有很多不同的信息。为事件日志的每个字段都创建一个列存储是非常不明智的,所以在字节事件日志的存储就两列 Event type(String类型)和 Message(Map类型)。所有的事件信息都是存放在 Message 中的。
嵌套数据类型的主要问题
嵌套类型的数据主要有以下四个问题:
•这些操作非常废 IO。比如我们有一个字段类型为 Map,其包含数十个子字段那么在读取这列时需要把整个列都读出来。并且 Spark 将遍历整个 map 获得目标键的值。•在读取嵌套类型列时不能利用向量化读取。•读取嵌套列时不能使用算子下推(Filter pushdown)。•大量的重复计算
优化解决方案
假设我们有一张名为 base_table 的表,里面有三个字段:item、count 以及 people,其中 people 字段是 map 类型的。现在我们要查询 people 字段里面的 age 字段。为了解决嵌套类型的查询慢问题,主要有两种方法来解决这个问题。
第一种方法就是创建一个名为 sub_table 的表。sub_table 表除了包含 base_table 的所有字段之外还新增了一个新字段 age,这个字段其实是 people 中 age 字段的值。所以如果我们要查询 people 里面的 age 字段,那么我们可以直接查询 sub_table 表里面的 age 字段,这样查询性能会比直接查询 base_table 表中 people 里面的 age 字段快。事实上,很多公司都在使用这种方法, 这种方法优点是由于嵌套列里面的字段被单独抽出来存储,查询直接在新的列里面进行,所以查询性能自然提升了。
•需要告诉下游所有使用到 base_table 的用户来使用 sub_table;
•sub_table 明显占用了重复的存储•不能处理频繁的子字段更改
•需要分别重构 Parquet 和 ORC 的向量化读取逻辑;
•Array/Map 数据类型的过滤下推目前还不支持;•嵌套类型数据的向量化读取性能没有简单数据类型的高
物化列是如何解决这些问题的
对于数据的读取,从上图的左边可以看出,对于非物化列的表,Spark 会读取 people 中读取 age 自动;对于物化列的表,Spark 会自动将 SQL 重写成对 age 自动的查询。
上图是物化列的查询性能指标。可以看出,对于物化列的查询性能均有很大提升,查询读的数据量大大减少。
Java与大数据架构
7年老码农,10W+关注者。【Java与大数据架构】全面分享Java编程、Spark、Flink、Kafka、Elasticsearch、数据湖等干货。欢迎扫码关注!
以上是关于物化列:字节为解决 Spark 嵌套列查询性能低下的优化的主要内容,如果未能解决你的问题,请参考以下文章
如何提高具有数组列的 DataFrame 的 Spark SQL 查询性能?
如何将具有嵌套StructType的列转换为Spark SQL中的类实例?