现今许多企业正在尝试使用一种新的数据管理范式来组织和管理数据——湖仓一体(Lakehouse),湖仓一体能够在非结构化数据湖之上实现结构化数据仓库的功能,从而使得数据能够在数据湖和数据仓库之间流动,企业可以在其中发掘更高的数据价值。然而,湖仓一体也为查询执行引擎带来了新的挑战,执行引擎不仅需要在结构化列存储数据上提供出色的性能,还要在非结构化的原始数据上保证良好平稳的性能表现。本次为大家带来数据库领域顶级会议SIGMOD的论文《Photon: A Fast Query Engine for Lakehouse Systems》。
如今的企业为了持续发掘和探索数据价值,会将绝大多数数据存储在可伸缩的弹性数据湖中(例如Azure数据湖存储)。这些数据湖以开放文件格式(例如Apache Parquet)保存原始的数据集,并通过各种引擎(例如Apache Spark)执行各类工作,如机器学习、SQL查询等。然而,这种方式需要预先对原始数据进行ETL处理并将处理后的数据送入数据仓库中才能获得高性能和高并发性,并且ETL过程还可能使得数据仓库中的数据与原始数据不同步。于是,许多企业开始尝试使用湖仓一体的方式存储数据。湖仓一体式架构直接在数据湖之上实现了ACID事务、SQL支持等数据仓库功能,具有简化数据管理、为用户提供统一的方法查询和管理数据等优点。为了最大化湖仓一体式架构的性能,论文提出了向量化查询引擎Photon。设计Photon主要面临两个关键挑战:其一是Photon需要在原始的、非结构化的数据上有良好的性能表现;其二是Photon需要支持现有的Apache Spark DataFrame API,并在查询语义上与之兼容。对于前者,论文的解决方案是使用向量化解释模型来构建引擎,并使用原生语言(C++)实现引擎;对于后者,论文的解决方案是使Photon与Spark高度集成,并通过严苛的测试保证语义一致。为了展示Photon如何与现有的湖仓一体式系统相融合,论文选择了Databricks Lakehouse平台作为示例。Databricks Lakehouse平台由四个主要组建组成:1. 原始数据湖存储层;2. 自动数据管理层;3. 弹性执行层;4. 用户界面。原始数据湖存储层。为了防止用户数据锁定以及避免成本高昂的跨平台数据迁移,Databricks Lakehouse平台将数据存储与计算解耦合,用户可以直接连接到云存储中现有的大型数据集。自动数据管理层。Databricks Lakehouse平台使用Delta Lake实现自动数据管理。Delta Lake是一个开源的基于云对象存储的ACID表存储层,支持ACID事务、时间旅行、审计日志和表格数据集上的快速源数据操作。弹性执行层。图1是弹性执行层的框架图,这一层实现了所有数据处理运行的“数据平面”。执行层主要负责执行“内部”查询(如自动数据聚类和元数据访问)和用户查询(如ETL作业、机器学习和SQL查询)。Photon通过在处理过的数据的每个分区上处理单线程查询来实现与执行层的融合。
Databricks运行时(Databricks Runtime,DBR)是处理所有查询工作的组件,支持Apache Spark的所有API。Photon位于DBR的最底层,在DBR的多线程无共享执行模型上下文中处理单线程任务。提交给DBR的应用称为作业,每个作业被分解为多个阶段,每个阶段又被分解成单独任务,而这些任务则在不同的数据分区上执行相同的代码。DBR中的阶段边界是阻塞的,即只有上一阶段结束后,下一阶段才能开始。这一特性允许通过重放阶段或者重新规划阶段边界的查询来支持容错或者自适应执行。DBR使用单个驱动节点进行调度、查询规划和其他集中式任务。每个驱动节点管理一个或者多个执行器节点,每个执行器节点负责执行具体的任务,扫描数据、处理数据和产生结果。SQL查询与其他所有查询共享相同的执行框架,并且可以构成一个或者多个作业,例如文件源数据查询和SQL子查询都可能作为单独的作业在同一个整体查询中进行。DBR中的驱动程序负责将SQL文本或者使用Apache Spark DataFrame API构造的DataFrame对象转换为查询计划。查询计划是一个映射到阶段列表的SQL操作符树(例如Filter,Project和Shuffle)。在进行查询规划之后,驱动程序启动任务,接着查询的每个阶段会被执行。每个执行的任务都使用内存中的执行引擎来处理数据,而该执行引擎就是Photon,Photon取代了之前基于Apache Spark SQL的引擎。Photon是一个使用原生语言(C++)编写的执行引擎,它一般被编译为一个共享库,并在DBR中被调用执行。在执行器的JVM进程中,Photon作为DBR中单线程任务的一部分执行。JVM与原生执行。 在先前的生产环境中,NvmeSSD缓存和自动优化的shuffle等底层优化显著降低了IO延迟,同时Delta Lake的数据聚类技术通过文件剪枝可以允许查询更多地跳过不需要的数据,进一步减少了IO等待时间。这些优化技术使得基于JVM的执行引擎所执行的内存中任务越来越成为性能瓶颈,但如果要继续优化性能,那么只能从JVM入手,通过非常复杂的手段保证JIT编译器产生最优代码并且可能需要手动管理堆外内存。综合上述的分析,原生语言实现可能更加适合。解释向量化与代码生成。现代高性能查询引擎主要遵循两种设计:1. 解释型向量化设计(例如MonetDB/X 100系统);2. 代码生成设计(例如Apache Spark、Hyper或者Apache Impala)。论文从以下几个方面考虑选择了解释型向量化设计:1. 更容易开发和拓展,代码生成方法由于需要手动注入代码,更加难以构建和调试;2. 更好的可观察性。向量化方法保持了操作符之间的抽象边界,并通过一次处理批量数据摊匀了开销;3. 更容易适应变化的数据。因为动态调度是解释向量化引擎的基础,所以引擎的执行模型适应性更强。向量化执行内核。Photon以向量的粒度调用运算符和表达式。每个内核将向量和列批处理位置列表作为输入,并产生一个向量作为输出。操作符将向量在其他操作符之间传递,直到最终从Photon中传递出去供外部使用(例如Apache Spark)。向量化哈系表。与标准的标量访问哈系表不同,Photon的哈系表针对向量化访问进行了优化。对哈系表的查找分三步进行。首先,使用散列内核在一批键上计算散列函数。然后,探测内核使用哈系值来加载指向哈系表条目的指针(哈系表中的条目按行存储,因此单个指针可以表示复合键)。最后,将哈系表中的条目逐列与查找键进行比较,并为不匹配的行生成位置列表。非匹配行根据探测策略,对已填满的桶进行桶索引,继续探索哈系表。向量内存管理。为了避免产生代价高昂的操作系统级的内存分配,Photon使用内部缓冲池为瞬态列批次分配内存,该缓冲池使用最近使用的机制进行内存和缓存分配。这样可以保持热内存的使用,以便为每个输入批次重复分配内存。
图2 Spark执行计划转换为Photon执行计划
将Spark计划转换为Photon计划。执行计划的转换是通过Spark SQL的可拓展优化器Catalyst中新规则来完成的(如图3)。Catalyst规则是应用于查询计划的模式匹配语句和相应替换的列表。如果一个模式在查询计划的一个节点上匹配,则该节点将被按照相应替换列表替换。执行Photon计划。查询规划完成后,DBR启动任务来执行计划的各个阶段。在使用Photon执行的任务中,Photon执行节点首先将计划的Photon部分序列化为Protobuf消息,然后将这个消息通过Java Native Interface(JNI)传递给Photon C++库,由Photon库反序列化Protobuf消息并将其转换为Photon-Internal执行计划并执行。确保语义一致性。为了确保同一查询Photon和Apache Spark的行为相同,论文使用了三种不同的测试去覆盖:1. 单元测试,明确列举测试用例,例如SQL表达式;2. 端到端测试,明确比较与Spark在SQL查询上的结果;3. 模糊测试,随机生成输入数据并将结果与Spark进行比较。一个8节点的AWS集群,每个节点都是一个具有64GB内存和8个vcpu(Intel Xeon E5 2686 v4)的实例。论文在上述集群中进行了TPC-H基准测试,并记录了预热运行后所有查询的三次运行的最小时间(如图3)。
图3 Photon与一般DBR的TPC-H基准测试表现对比
总的来说,Photon实现了23倍的最大加速比,在所有查询上的平均加速比为4倍。为了进一步验证Photon性能,TPC委员会也在启用了Photon的DBR上独立运行了完整的TPC-DS基准测试,其性能截至2022年2月仍保持着TPC世界记录。Photon是一个用于Lakehouse系统的向量化查询引擎。Photon的原生设计解决了基于JVM的执行引擎所面临的可拓展性和性能问题,其矢量化处理模型实现了快速开发、指标报告和微自适应执行,能够很好地处理数据湖中无处不在的非结构化原始数据。通过标准化基准测试和微基准测试,论文证明了Photon在SQL工作负载上已经达到了最先进的性能。本文作者 陈泽超 重庆大学物联网工程专业在读二年级本科生,重庆大学START团队成员。主要研究方向:时空数据流式查询。 | |
时空艺术团队(START,Spatio-Temporal Art)来自重庆大学时空实验室,旨在发挥企业和高校的优势,深入探索时空数据收集、存储、管理、挖掘、可视化相关技术,并积极推进学术成果在产业界的落地!年度有2~3名研究生名额,欢迎计算机、GIS等相关专业的学生报考!