这篇文章是来字节后写的第一篇输出文章,已经同步发布在字节的公众号平台上。

前言

Apache Hive 经过多年的发展,目前基本已经成了业界构建超大规模数据仓库的事实标准和数据处理工具,Hive已经不单单是一个技术组件,而是一种设计理念。Hive有JDBC客户端,支持标准JDBC接口访问的HiveServer2服务器,管理元数据服务的Hive Metastore,以及任务以MapReduce分布式任务运行在YARN上。

标准的JDBC接口,标准的SQL服务器,分布式任务执行,以及元数据中心,这一系列组合让Hive完整的具备了构建一个企业级数据仓库的所有特性,并且Hive的SQL服务器是目前使用最广泛的标准服务器。

虽然Hive有非常明显的优点,可以找出完全替代Hive的组件寥寥无几,但是并不等于Hive在目前阶段是一个完全满足企业业务要求的组件,很多时候选择Hive出发点并不是因为Hive很好的支持了企业需求,单单是因为暂时找不到一个能支撑企业诉求的替代服务。

企业级数仓构建需求

数仓架构通常是一个企业数据分析的起点,在数仓之下会再有一层数据湖,用来做异构数据的存储以及数据的冷备份。但是也有很多企业,特别是几乎完全以结构化数据为主的企业在实施上会把数据湖和企业数仓库合并,基于某个数仓平台合二为一。

企业在考虑构建自身数仓体系的时候,虽然需要参考现有的行业技术体系,以及可以选择的组件服务,但是不能太过于局限于组件本身,寻找100%开箱即用的产品。太过于局限于寻找完全契合的组件服务必然受限于服务本身的实现,给未来扩展留下巨大的约束。企业数据仓库架构必然不等于一个组件,大部分企业在数仓架构实施的都是都是基于现有的部分方案,进行基于自己业务合适的方向进行部分开发与定制,从而达到一个半自研的稳态,既能跟上业务变化的速度,又不过于依赖和受限于组件自身的发展。

一般来说企业级数仓架构设计与选型的时候需要从以下几个纬度思考:

  • 开发的便利性:所选择的数仓架构是否具有很好的开发生态,可以提供不同类型的开发态接口,不限于SQL编辑器,代码提交,以及第三方工具整合。
  • 生态:所选择实现引擎自身是否有很好的生态功能,或者是否可以很好的与其他服务集成,例如数据湖引擎delta lake,icebeg,hudi等优秀组件出现,但是Hive集成的节奏却非常慢。
  • 解耦程度:分布式任务必然需要多个组件的协调,例如分布式存储,资源管理,调度等,像Hive就重度依赖于YARN体系,计算引擎也与MR强绑定,在解耦方面较弱,如果企业考虑在K8S上构建自己的计算引擎,Hive面临的局限会更加明显。
  • 性能:整体架构是否拥有更好的性能。
  • 安全:是否支持不同级别,不同力度的用户访问和数据安全鉴权体系。

对于企业数仓架构来说,最重要的是如何基于企业业务流程来设计架构,而不是基于某个组件来扩展架构。

一个企业数仓的整体逻辑如上图所示,数仓在构建的时候通常需要ETL处理和分层设计,基于业务系统采集的结构化和非结构化数据进行各种ETL处理成为DWD层,再基于DWD层设计上层的数据模型层,形成DM,中间会有DWB/DWS作为部分中间过程数据。

从技术选型来说,从数据源的ETL到数据模型的构建通常需要长时任务,也就是整个任务的运行时间通常是小时及以上级别。而DM层主要是支持业务的需求,对实效性要求比较高,通常运行在DM层上的任务时间在分钟作为单位。

基于如上的分层设计的架构图可以发现,虽然目前有非常多的组件,像Presto,Doris,ClickHouse,Hive等等,但是这些组件各自工作在不同的场景下,像数仓构建和交互式分析就是两个典型的场景。

交互式分析强调的是时效性,一个查询可以快速出结果,像Presto,Doris,ClickHouse虽然也可以处理海量数据,甚至达到PB及以上,但是主要还是是用在交互式分析上,也就是基于数据仓库的DM层,给用户提供基于业务的交互式分析查询,方便用户快速进行探索。由于这类引擎更聚焦在交互式分析上,因此对于长时任务的支持度并不友好,为了达到快速获取计算结果,这类引擎重度依赖内存资源,需要给这类服务配置很高的硬件资源,这类组件通常有着如下约束:

  • 没有任务级的重试,失败了只能重跑Query,代价较高。
  • 一般全内存计算,无shuffle或shuffle不落盘,无法执行海量数据。
  • 架构为了查询速度快,执行前已经调度好了task执行的节点,节点故障无法重新调度。

一旦发生任务异常,例如网络抖动引起的任务失败,机器宕机引起的节点丢失,再次重试所消耗的时间几乎等于全新重新提交一个任务,在分布式任务的背景下,任务运行的时间越长,出现错误的概率越高,对于此类组件的使用业界最佳实践的建议也是不超过30分钟左右的查询使用这类引擎是比较合适的。

而在离线数仓场景下,几乎所有任务都是长时任务,也就是任务运行时常在小时及以上,这时就要求执行ETL和构建数仓模型的组件服务需要具有较高的容错性和稳定性,当任务发生错误的时候可以以低成本的方式快速恢复,尽可能避免因为部分节点状态异常导致整个任务完全失败。

可以发现在这样的诉求下类似于Presto,Doris,ClickHouse就很难满足这样的要求,而像Hive,Spark 这类计算引擎依托于Yarn做资源管理,对于分布式任务的重试,调度,切换有着非常可靠的保证。Hive,Spark等组件自身基于可重算的数据落盘机制,确保某个节点出现故障或者部分任务失败后可以快速进行恢复。数据保存于HDFS等分布式存储系统上,自身不管理数据,具有极高的稳定性和容错处理机制。

反过来,因为Hive,Spark 更善于处理这类批处理的长时任务,因此这类组件不擅长与上层的交互式分析,对于这种对于时效性要求更高的场景,都不能很好的满足。所以在考虑构建数仓的时候,通常会选择Hive,Spark等组件来负责,而在上层提供交互式分析查询的时候,通常会使用Presto,Doris,ClickHouse等组件。

归纳下来如下:

  • Presto,Doris,ClickHouse:更注重交互式分析,对单机资源配置要求很高,重度依赖内存,缺乏容错恢复,任务重试等机制,适合于30分钟以内的任务,通常工作在企业的DM层直接面向业务,处理业务需求。
  • Hive,Spark:更注重任务的稳定性,对网络,IO要求比较高,有着完善的中间临时文件落盘,节点任务失败的重试恢复,更加合适小时及以上的长时任务运行,工作在企业的的ETL和数据模型构建层,负责清洗和加工上层业务所需要的数据,用来支撑整个企业的数仓构建。

一个企业在实施数据平台的时候,由多个不同组件各自工作在不同的架构层中,无法相互取代,相互协作配合,承载整个企业的数据平台业务。

企业级数仓技术选择

Google发表的三篇论文从存储,计算,检索三个方向阐述了海量数据下一种新的分布式数据加工处理技术,这三个方向被雅虎Nutch团队实现后贡献给Apache,也就是目前大家看到的HDFS,MapReduce和HBase,形成了早期Hadoop的三大利器。

然而这三大利器更聚焦在异构数据的信息提取处理上,没有提供对结构化数据很友好的类似SQL语法的分析入口,同时在编程态的支撑也不够友好,只有Map和Reduce两阶段,严重限制了业务处理的实现,雅虎团队也是爬虫相关业务孵化而出,可以看出Hadoop早期的三大套件有着如下特点:

  • 门槛高,需要编程实现,并且编程态受限于MapReduce的两阶段约束。
  • 以离散数据处理为主,对分析能力,查询等常用数据分析功能支持不足。
  • 没有交互式客户端,无法实现交互式探索。

Hive就是诞生在这样的较大的行业背景下,Hive的出现刚好弥补了Hadoop只能用来做离线数据处理这个缺陷,提供了一种常用的分析接口,并且提供了非常好的用户交互方式。

Hive整体架构如上图所示,Hive提供JDBC接口实现支持以编程形式进行交互,客户端, 几乎所有SQL Client、开源或商业BI工具都支持通过标准JDBC的方式连接Hive,可以支持数据探索的动作,极大的丰富了大数据生态圈下的组件多样性,同时也降低了使用门槛,可以让熟悉SQL的人员低成本迁移。

基于这些设计非常好的特效,加上Hive经过这多年的逐步完善,发展到今天已经是一个非常稳定成熟的生产环境可用的数据仓库组件,甚至替代品都很难找到,因此使用Hive作为数据仓库的构建基础是一个非常好的选择。

如上图所示,其中有很多优点:

  • 稳定:稳定性是Hive一个非常让人称道的特性,很多时候虽然Hive的性能,计算速度不及其他引擎,但是Hive的稳定性却一直是非常好的。
  • 低门槛:只需要掌握基本的SQL技能,便可使用Hive进行开发,相比其他分布式计算引擎引擎成本更低。
  • 生态丰富:Hive和Hadoop生态圈紧密结合,而Hive自身的Metastore也成了大数据生态圈内的标准元数据服务,大部分引擎都支持直接适配MetaStore。
  • 扩展方便:Hive自身的UDF机制可以快速基于业务需要扩展功能。
  • 安全:Hive支持Kerberos/LDAP多种认证方式,并且和Ranger结合可以做到更细粒度的行列权限级别,拥有较好的数据安全。
  • 集成成本低:MapReduce只支持编程态的接口,并且不支持迭代计算,Hive封装了MapReduce提供SQL的接口,可以很低成本的和上层数据挖掘,数据分析工具进行集成。

所以虽然Hive出现已经非常有很长时间了,但是依旧是数仓构建的首选,在整个数仓构建中随处可见Hive的身影。虽然Hive有种种优点,让人难以割舍,但是并不等于能很好的支撑企业业务需求。很多时候选择Hive仅仅是因为暂时没有其他可选的组件,如果自己从头开发一个,或者基于某个组件改造,成本又会远超企业预期,因此不得不继续选择使用Hive。

基于实践来看,Hive在构建企业数仓过程中存在的主要局限围绕在以下几个方面:

  • 性能:Hive基于MapReduce虽然带来了非常好的稳定性,同时也降低了它的性能,虽然有TEZ做一定的优化,但是与同类的计算引擎Spark相比依旧有非常大的差距。
  • 资源配置:由于Hive底层使用MapReduce作为计算引擎,而MapReduce对SQL不友好,因此Hive在HiveServer2层面实现了SQL的转换处理,再生成基于MapReduce的物理计划,从而导致HiveServer2需要非常高的配置,才能维持足够好的稳定性。
  • 并发:Hive的并发受限于HiveServer2,企业需要维护多个高配的HiveServer2实例才能支持更好的并非,通常Hive的瓶颈都在HiveServer2而不是更底层的分布式计算。
  • 容错成本:Hive基于HiveServer2进行SQL的分析处理,多个HiveServer2之间相互独立不共享信息,因此当HiveServer2挂掉后,整个HiveServer2的任务都会结束,需要客户端自行重试,为整个作业级别的容错重启。
  • 事务支持:Hive的事务设置在HiveServer2上,一旦HiveServer2实例开启事务后,整个通过该HiveServer2的请求都会开启事务,整个事务成本过高。
  • 部署:如果企业的计算引擎部署是基于K8S等容器架构,Hive on K8S将会带来非常大的部署成本。

虽然Hive在以上局限层面也做了很多尝试,Hive On Spark,但是受限于Hive的架构,HiveServer2自身有自己的SQL解析引擎,为了兼容架构将解析后的结果直接翻译成Spark最底层的接口,整体性能反而提升不大。

除了Hive之外,还有非常多的其他优秀的组件,但是从企业数仓技术选型的视角来看,适合用来构建数据仓库的,目前只有Hive和Spark SQL相对更加合适,在这两个组件中,Spark SQL相对Hive的优势又更加明显。

SparkSQL如何支撑企业级数仓

Spark引擎因为自身强大的生态和方便的编程接口被广泛应用在数据处理场景下,Spark 提供的Spark SQL模块更是将使用Spark支撑企业数据仓库提供了一个良好的基础设施。

如上图所示,一个典型的数据仓库架构需要包含不同层次的模型构建。由于数据量大,数据结构异构等多种原因,大数据架构下的企业数仓构建抛弃了基于关系型数据库下的Cube设计,直接采用基于分布式任务进行处理来构建多层数据模型。因此对于构建企业数仓的服务来说,有着如下要求:

  • 支持长时任务,通常是小时以上,天级别居多。
  • 支持多任务,也就是高并发。
  • 稳定性必须被保障。
  • 速度快。
  • 支持SQL的交互式接口。
  • 易于集成。
  • 支持任务的重跑和容错以及快速任务失败恢复。

基于以上特性可以发现,在目前可选择的组件范围内,Spark SQL相比其他组件,乃至Hive更加合适承担这类任务。但是很多企业在进行架构设计的时候割舍不掉Spark SQL带来的丰富特性,又愁于Spark SQL缺乏类似Hive这样的SQL服务器,于是退而求其次变成Hive与Spark SQL两个组件共存的形态,Hive退化为仅仅提供MetaStore服务,因此从很多实践的现象来看,Hive构建企业数仓已是过去式,采用Spark SQL进行数据仓库的构建是众多的选择。

如上图所示,企业在构建数仓的时候,通过一个Spark SQL Server提供基于SQL接口的常驻服务,同时也可以采用Spark Submit的方式直接提交Jar任务去运行,既能达到提供标准SQL交互式接口,又能提供更灵活的编程态接口。

从不同的企业级数仓构建视角来看,Hive带来的约束都越来越大,而Spark SQL的成熟度和发展趋势已经完全具备取代Hive来构建整个数仓,Spark SQL的优势集中体现在如下方面:

  • 丰富的生态:Spark不仅可以和很多组件集成,其自身拥有生态已经涵盖各个方面,从数据分析到机器学习和图计算。
  • 开放:Spark架构设计上非常开放,可以快速整合其他产品,例如相比Hive,在集成Iceberg,Hudi等特性方面就会开放很多。
  • 部署:Spark既可以部署在ECS虚拟机上,也可部署在K8S架构上,多种部署形态非常灵活。
  • 性能:Spark的机制的流批处理性能非常合适用来构建企业数仓。
  • 易于开发:Spark SQL既有SQL接口,也支持灵活的可迭代编程接口,非常方便不同场景下的数据开发。
  • 安全:Spark SQL 可和不同的安全服务集成,实现细粒度的鉴权。

因此,完全基于使用Spark SQL来支撑企业级的数仓是完全可行的,并且在目前也被众多企业实践验证。

如上图所示,一个基于Spark SQL构建的企业数仓架构逻辑架构设计上包含以上几个部分,每一个Spark SQL 引擎都是一个服务器,Spark SQL引擎将自己的信息注册到Zookeeper中,SQL服务器基于Zookeeper中的Spark SQL引擎来执行客户端过来的请求,SQL 服务器是一个兼容Hive JDBC接口的服务器,在使用Spark SQL来支撑数仓构建的时需要重点考虑的实施点是:

  • 如何提供一个交互服务用来支撑不同的客户端来连接,包括交互式的beeline,以及编程态的JDBC和工具接口。
  • 如何打通权限对接,如果是Ranger的话需要的是Spark SQL Ranger Plugin。
  • 如何支持跨多个队列的任务提交。

使用Spark SQL支撑企业级数仓的核心的地方还是在于如何提供一个好用的任务服务器,用来支撑任务的管理。任务管理服务器在逻辑上与HiveServer2相似,但是更加的轻量,没有HiveServe2中复杂而繁重的SQL解析,同时又没有Spark Thrift Server这种自身就是一个YARN作业的约束。企业可以基于自身的业务流程,开发一个轻量的服务器,在这方面字节有非常深的实践经验,同时也有自己的Spark SQL 引擎服务器,可关注后续的动态。同时业界也有其他企业做了类似的工作,例如网易开源的Kyuubi。

Kyuubi整个架构图如上所示,Kyuubi基于Spark SQL之上,较好的弥补了Spark Thrift Server在多租户、资源隔离和高可用等方面的不足,是一个真正可以满足大多数生产环境场景的开源项目。但是Kyuubi在设计的时候考虑的是如何弥补Spark Thrift Server的不足,目的在于增强Spark SQL的能力,而不是对等设计一个可以替换Hive组件的服务。因此对于遗留项目来说迁移成本较高,Spark SQL与Hive有着两套不兼容的SQL,在使用Kyuubi的时候如何是遗留系统迁移成本将是一个非常大的工作量。

而行业也有开源的Spark Thrift Server,该思路是非常优秀的,但是因为开发过程中有点太过于局限,导致依旧存在很多问题,主要体现在:

  • Driver单点:整个Spark thrift server以一个Spark任务的形式运行在YARN上,所有的请求都运行在一个Driver中,一旦Driver挂掉后,所有任务都会同时失败。
  • 资源隔离:因为Spark thrift server是以Spark任务的形式运行在YARN上,因此提交的任务如果有跨队列提交需求的时候,Spark thrift server很难支撑,其次多个任务运行在同一个Driver之中,资源使用会相互影响,很难更精细化的进行资源的管理。
  • 多租户:Spark thrift server从请求层面是可以支持多用户的,但是从架构层面来看Spark thrift server是一个运行在Yarn上的任务,它也有自己的Application Id有自己的任务提交者,因此它实际上是以一个超级管理员的身份运行,再做二次租户隔离,必然存在一定的资源安全问题。
  • 高可用:Spark thrift server本身是没有高可用涉及的,因此它的高可用需要自行单独设计,且还得考虑客户端的兼容,例如Hive JDBC将HA信息存储在ZK中,而Spark thrift server没有这样的机制,因此高可用的实施成本较高。

因此虽然Spark提供了Spark thrift server服务用来提供类似JDBC这样的接口交互方式,但是目前依旧缺乏很多生成功能,导致在生产环境使用的情况非常少,Spark thrift server更像是一个小众的半成品,小修小补的尝试着解决部分问题,但是没有给予一个彻底的方案,导致现在有点缺乏实际的生产应用。

字节跳动EMR产品在Spark SQL的优化实践

数据湖引擎集成

Hudi,Iceberg等数据湖引擎目前使用的越来越广泛,很多B端客户在使用Spark SQL的时候也存在需要使用数据湖引擎的需求,因此字节EMR产品需要将数据湖引擎集成到Spark SQL中,在这个过程碰到非常多的问题。

首先在与Iceberg集成的时候,对体验和易用的问题进行了优化,用户在使用Spark SQL过程中,需要手动输入很多指令,并且需要找到对应的spark-iceberg 依赖包,这个也是目前集成Iceberg最常用的方案。我们的解决方式是在预先安装的过程中,提前把iceberg的相关jar包放到spark jars目录下,这样用户只需要指定catalog即可,无需再手动输出很多指令。

其次在Spark与Hive跨引擎分析场景下使用Iceberg,Spark正常创建表,Presto/Trono可以正常读写,但Hive无法正常读写,这个问题官方的文档也没有清晰的描述,解决方案是需要修改Spark的配置文件或者修改Hive的hive-site-spark override配置,确保初始化出来的Spark Session中的配置项iceberg.engine.hive.enable的值为true,Hive才能正常的读取Spark创建的表。

问题上本质上是由于Iceberg为了支持Hive引擎,在整体的设计上做了一些妥协,使用了Storage Handler的方式去实现Hive对Iceberg格式的表的读写,需要显式的指定Hive的Input/Output Format实现,而Presto/Trono则可以基于Hive的format_type自动识别表的格式进行识别。

在兼容性上,由于Iceberg 0.12版本不支持Spark 3.2,由于升级Spark的影响范围非常大,于是更新了Iceberg,使用了社区的一个master的snapshot版本进行编译,与Spark 3.2进行集成。

Spark SQL服务器

虽然行业针对Spark SQL 提供一个SQL 服务器已经有Spark Thrift Server或者Kyuubi这样的工具,但是在某些B端客户的业务的背景下,这些工具并不能完全满足要求,因此字节跳动EMR团队自己设计实现了Spark SQL Server,主要聚焦解决的是如下场景:

  • 兼容Hive语义:由于大部分B端客户早期是基于Hive构建的数据仓库,后续逐步全部替换为Spark SQL,中间必然面临大量的系统迁移,而由于Hive与Spark SQL语义不尽相同,重写SQL实现的工作量非常大,因此在字节EMR产品中的Spark SQL Server中实现Hive 语义和Spark SQL语义的兼容,在实现方案上采用的时候讲Hive SQL解析注入到Spark 引擎中,形成一个SQL Parser Chain,最终会匹配到某一个解析器,实现对SQL的解析,从而达到对整个SQL语义的兼容。
  • 提前初始化Spark SQL引擎:在业务请求到达前提前在YARN上提交Spark任务,初始化资源信息,让整个引擎处于等待的状态,可以减少任务提交消耗的时间,在用户较多的情况下可以提示整体的任务执行时间。
  • 跨Yarn队列的任务提交:用户可以指定Yarn队列执行任务。

如上图所示,SQL服务器是一个实现了Thrift 接口的服务器,提供标准的JDBC访问接口,Spark SQL引擎同样实现了Thrift 接口,Spark SQL引擎在服务启动的时候便已经被提交至Yarn,处于等待状态。当业务任务到达的时候,由SQL服务器实现引擎的筛选,匹配一个已经存在的引擎,或者重新提交一个全新的引擎用来执行任务。

SQL 服务器支持OpenLDAP,Kerberos等常用的权限认证,同时支持多种不同的隔离级别,例如Session级别则每一个业务SQL都会初始化一个Spark SQL引擎用来接收任务,任务执行结束后,引擎从Yarn中销毁。而User级别则针对用户会初始性0-N个引擎,常驻于Yarn中,处于交替执行任务。

这样的服务器设计打破了Spark Thrift Server的单Driver所带来的局限,解耦了SQL服务和任务执行。也就支持更细粒度的资源管理和跨队列的任务提交。

同时也兼容了Hive的接口,用户可以通过如下方式访问服务器:

  • HA访问链接:
 ./bin/beeline -u   jdbc:hive2://emr-5fqkwudj144d2gc1k8hi-master-1/;serviceDiscoveryMode=zooKeeper;zooKeeperNamespace=midas/ha;auth=LDAP   -n emr_dev -pEMR123456emr
  • 非HA访问链接:
./bin/beeline -u   jdbc:hive2://emr-master-2:10005/default;auth=LDAP”  -n test_sub -pEMR123456emr

HA模式下的信息被记录在Zookeeper中,保存的内容格式与HiveServer2的内容一致,能确保使用Hive的客户端可以直接访问HA模式下的服务器。

Spark SQL多租户

在Hive任务执行过程中,HiveServer2服务承担了提供SQL服务器进行用户身份认证,权限判断,以及解析SQL生成最终的执行计划,再由MR引擎执行具体的分布式任务。

在这个过程中HiveServer2承担了非常重的职责,因此需要消耗非常大的资源,因此会很大程度的影响用户的并发。对于分布式任务运行来说,它的资源约束来自于Yarn作为资源管理器所分配的资源,但是在Hive架构下却受限于HiveServer2的影响,导致用户并发的数量无法随着Yarn资源的提升进行提升。

而在Spark SQL引擎中,SQL解析是下推到引擎内部,与具体的分布式任务执行合为一体,不需要单独的服务器去做SQL解析。也正因为Spark SQL与Hive在解析模块的架构存在差异,Hive On Spark的模式会变得非常难。

针对如上的场景,字节跳动EMR团队重新设计的SQL服务器只负责任务的接收,进行用户资源,权限和身份的判断,然后将任务发送给运行在Yarn中的Spark SQL服务器。打破了Hive这种并发受制于HiveServer2和Yarn两层约束的局面,只由Yarn的资源决定用户的并发程度,从而极大的降低了Spark SQL服务器的资源需求,增强了其稳定性,在用户并发上有了非常大的提升。

其次通过引擎预热的功能减少任务执行的时间,提升整体速度,引擎预热指的是在服务启动的时候便向Yarn提交Spark SQL引擎,处于等待的状态,当业务请求到达的时候,基于业务类型从已经处于就绪的引擎中选择一个引擎来执行任务。

并不是每一个预热提交的引擎都会被选择执行,在SQL服务器中存在如下三种引擎隔离级别:

  • Session:基于每一个connection都会全新提交Spark SQL引擎,在链接断开后,引擎从Yarn上销毁。
  • User:同一个用户可以共享多个Spark SQL引擎,具体的Spark SQL引擎个数由该用户提交的任务资源需求决定,引擎在连接断开后不会销毁,直到引擎空闲时长到达上限。
  • Open:所有用户都可共享的Spark SQL引擎,通常是用来应对大账号,或者集群不需要做权限管理的场景。

由此可见只有在User,Open的级别下引擎预热才会产生价值,预热省去了Spark Submit的时间,当用户数量足够多,群体为统计单位所节省的时间越多。

Spark SQL事务支持

Hive的事务力度是基于HiveServer2实现的,例如通过如下语法:

CREATE TABLE tm (a int, b int) stored as orc TBLPROPERTIES
('transactional'='true', 'transactional_properties'='insert_only')

可开启事务,但是由于对事务的管理是在服务器上,因此需要开启ACID的时候受影响的是整个HiveServer2的所有请求,而Spark SQL很好的集成和支持了Hudi,Iceberg等数据湖格式,因此在Spark SQL服务器中不需要实现类似HiveServer2的事务机制,只需要在最终读取处理数据的时候,采用Hudi,Iceberg等特性便可达到支持事务的效果。

例如对于Icdberg数据格式的表已支持update、delete操作:

MERGE INTO prod.nyc.taxis ptUSING (SELECT * FROM staging.nyc.taxis) stON pt.id = st.idWHEN NOT MATCHED THEN INSERT *;

因此当Spark SQL集成了Iceberg后,便具有了事务能力,再配合SQL服务器,便可在很低的成本上具有和Hive事务能力同等的效益,同时也没有Hive下的一些约束,从这样的架构设计上来看,能够完整的替换Hive。另外一个方面当用户数量变多,同时数据会发生修改,更新等操作,很容易造大量的小广播传输,从而引起Driver的OOM。虽然大广播也会存在OOM的问题,但是大广播可以通过阈值控制,而小广播阈值对其不生效,一旦说数量变多,很容易引起Driver的OOM。字节EMR团队通过对小广播进行合并广播,解决大量小广播进行传播,导致打爆Driver的情况出现。

尾声

随着企业的业务发展越来越复杂,需要更加灵活,更加高效的数仓架构,在这样的业务驱动背景下,Hive的局限变得越来越明显,而基于Spark SQL灵活构建数仓的方案将会变得越来越主流。所以企业在考虑数据仓库构建体系的时候,可以考虑如何基于Spark SQL构建自身数据体系,Spark 完善和开放的生态在未来必然会有更多优秀的服务会围绕Spark形成强大的优势。


扫码手机观看或分享: