Spark大数据商业实战三部曲:内核解密、商业案例、性能调优(第2版)
上QQ阅读APP看书,第一时间看更新

2.3 Spark 2.4 SQL

Spark 2.0通过对SQL 2003的支持增强了SQL功能,Catalyst新引擎提升了Spark查询优化的速度;本节对DataFrame和Dataset API、时间窗口进行了讲解。

在Apache Spark 2.2.X版本的基础上,Apache Spark 2.3.X版本、Apache Spark 2.4.X版本中核心和Spark SQL的主要更新如下。

1.API更新

 SPARK-18278:Spark基于Kubernetes。一个新的Kubernetes调度器后端,支持将Spark作业提交到Kubernetes进行集群管理。这种支持目前是实验性的,应该对配置、容器映像和入口点进行更改。

 SPARK-16060:矢量化ORC阅读器。增加了对新ORC阅读器的支持,通过矢量化(2~5倍)大大提高了ORC扫描吞吐量。要启用阅读器,用户可以将spark.sql.orc.impl设置为native。

 SPARK-18085:Spark History Server V2。一个新的Spark History Server(SHS)后端,通过更高效的事件存储机制为大规模应用程序提供更好的可扩展性。

 SPARK-15689、SPARK-22386:Data Source API V2。在Spark中插入新数据源的实验API。新的API试图解决V1版本API的几个局限性,旨在促进高性能、易于维护和可扩展的外部数据源的开发。此API仍在进行开发。

 SPARK-22216、SPARK-21187:通过快速数据序列化和矢量化执行显著提高了Python的性能和互操作性。

 SPARK-26266:对Scala 2.12.8更新(需要最近的Java 8版本)。

 SPARK-26188:Spark 2.4.0分区行为打破向后兼容性。

 SPARK-27198:driver和executor中的心跳间隔不匹配。

 SPARK-24374:屏障执行模式。在调度器中支持屏障执行模式,更好地与深度学习框架集成。

 SPARK-14220:可以使用Scala 2.12构建Spark,并在Scala 2.12中编写Spark应用程序。

 SPARK-23899:高阶函数。添加许多新的内置函数,包括高阶函数,以便更容易地处理复杂的数据类型。

 SPARK-24768:内置AVRO数据源。内嵌Spark-Avro包,具有逻辑类型支持、更好的性能和可用性。

 SPARK-24035:透视转换的SQL语法。

 SPARK-24940:用于SQL查询的Coalesce和Repartition提示。

 SPARK-19602:支持完全限定列名的列解析。

 SPARK-21274:实现EXCEPT ALL及INTERSECT ALL。

2.性能优化和系统稳定性

 SPARK-21975:基于成本的优化器中的直方图支持。

 SPARK-20331:更好地支持Hive分区修剪的谓词下推。

 SPARK-19112:支持ZStandard标准压缩编解码器。

 SPARK-21113:支持预读输入流在溢出读取器中分摊磁盘I/O成本。

 SPARK-22510、SPARK-22692、SPARK-21871:进一步稳定codegen框架,以避免Java方法和Java编译器常数池受到64KB JVM字节码限制。

 SPARK-23207:修复了Spark中一个长期存在的BUG,在这种情况下,数据帧上的连续洗牌+重新分区可能导致某些案例的不正确。

 SPARK-22062、SPARK-17788、SPARK-21907:解决OOM的各种原因。

 SPARK-22489、SPARK-22916、SPARK-22895、SPARK-20758、SPARK-22266、SPARK-19122、SPARK-22662、SPARK-21652:基于规则的优化器和规划器中的增强功能。

 SPARK-26080:无法在Windows上运行worker.py。

 SPARK-27419:如果设置值spark.executor.heartbeatinterval少于1秒,将经常失败,因为值将被转换为0,心跳将始终超时,最终杀死executor。

 SPARK-25535:解决常见加密中的错误检查。

 SPARK-26891:RequestExecutors反映节点黑名单并可序列化。

 SPARK-27496:RPC返回致命错误。

 SPARK-27544:修复Python测试脚本以在Scala 2.12版本上工作。

 SPARK-27469:将commons beanutils更新为1.9.3。

 SPARK-16406:大量列的引用解析应该更快。

 SPARK-23486:lookupFunctions从外部目录缓存函数名以查找函数。

 SPARK-23803:支撑桶修剪。

 SPARK-24802:优化规则排除。

 SPARK-4502:Parquet表的嵌套方案修剪。

 SPARK-24296:支持大于2GB的复制块。

 SPARK-24307:支持从内存发送超过2GB的消息。

 SPARK-23243:随机洗牌+对RDD重新分区可能导致错误答案。

 SPARK-25181:限制了BlockManager主线程池和从线程池的大小,在网络速度较慢时降低了内存开销。

3.更新变化

 SPARK-22036:默认情况下,如果无法精确表示,小数之间的算术运算将返回舍入值(以前的版本中返回空值)。

 SPARK-22937:当所有输入都是二进制的时候,sql elt()返回一个二进制输出。否则将作为字符串返回。在以前的版本中,不管输入类型如何,总是以字符串形式返回。

 SPARK-22895:如果可能,在第一个非确定性谓词之后的连接/筛选器的确定性谓词也会向下通过子运算符推送。在以前的版本中,这些过滤器不符合谓词下推的条件。

 SPARK-22771:当所有输入都是二进制的时候,functions.concat()返回一个二进制输出。否则,它将作为字符串返回。在以前的版本中,不管输入类型如何,它总是以字符串形式返回。

 SPARK-22489:当任何一个join端可以广播时,广播提示中显式指定的表。

 SPARK-22165:分区列推断以前发现不同推断类型的公共类型不正确,例如,以前它以double类型作为double类型和date类型的公共类型结束,现在,它为此类冲突找到了正确的通用类型。

 SPARK-22100:percentile_approx函数以前接受numeric数字类型输入,并输出double类型结果。现在支持日期类型、时间戳类型和数字类型作为输入类型,结果类型也更改为与输入类型相同,这对百分位数更为合理。

 SPARK-21610:当引用的列只包含内部损坏记录列(默认情况下名为“损坏记录”)时,不允许从原始JSON/CSV文件查询。相反,可以缓存或保存解析的结果,然后发送相同的查询。

 SPARK-23421:Spark 2.2.1和2.3.0,当数据源表具有同时存在于分区模式和数据模式中的列时,总是在运行时推断模式。推断模式没有分区列。读取表时,Spark将查询这些重叠列的分区值,而不是数据源文件中存储的值。在2.2.0和2.1.X版本中,推断的模式是分区的,但用户看不到表的数据(结果集为空)。

 SPARK-19732:na.fill()或fillna也接受布尔值,并用布尔值替换空值。在以前的Spark版本中,pyspark只是忽略它并返回原始的数据集/数据帧。

 SPARK-22395:Pandas 0.19.2或更高版本需要使用Pandas相关功能,如toPandas、从Pandas DataFrame创建数据帧等。

 SPARK-22395:与panda相关功能的时间戳值的行为已更改为会话时区,这在以前的版本中被忽略。

 SPARK-23328:在to_replace不是字典时,df.replace不允许省略值。以前,值在其他情况下可以省略,默认情况下没有值,这是反直觉的,并且容易出错。

 SPARK-20236:支持Hive动态分区覆盖语义。

 SPARK-4131:支持INSERT OVERWRITE DIRECTORY,直接从查询将数据写入文件系统。

 SPARK-19285、SPARK-22945、SPARK-21499、SPARK-20586、SPARK-20416、SPARK-20668:UDF增强。

 SPARK-20463、SPARK-19951、SPARK-22934、SPARK-21055、SPARK-17729、SPARK-20962、SPARK-20963、SPARK-20841、SPARK-17642、SPARK-22475、SPARK-22934:改进了ANSI SQL兼容性和Hive兼容性。

 SPARK-20746:更全面的SQL内置函数。

 SPARK-21485:为内置函数生成Spark SQL文档。

 SPARK-19810:移除对Scala 2.10的支持。

 SPARK-22324:将Arrow升级到0.8.0,neNetty升级到4.1.17。

 SPARK-25250:可能导致job永久挂起,将在2.4.2中恢复。

 SPARK-24935:从Spark 2.2开始执行Hive UDF时出现问题。

 SPARK-27539:修复包含空值的列的聚合输出行估计不准确的问题。

 SPARK-27563:自动获取HiveExternalCatalogVersionsSuite中的最新Spark版本。

 SPARK-24596:非级联缓存无效。

 SPARK-23880:不触发任何用于缓存数据的作业。

 SPARK-23510:支持Hive 2.2和Hive 2.3元存储。

 SPARK-23711:为UnsafeProjection添加fallback生成器。

 SPARK-24626:Analyze Table命令中的并行位置大小计算。

4.Bug修复

 SPARK-26961:在Spark Driver中发现Java级死锁。

 SPARK-26998:在独立模式下执行executor进程时,spark.ssl.keyStorePassword在“ps -ef”输出是明文形式。

 SPARK-27216:将RoaringBitmap升级到0.7.45以修复kryo不安全的ser/dser问题。

 SPARK-27244:使用选项logconf=true时修改密码。

 SPARK-27267:Snappy 1.1.7.1在解压缩空的序列化数据时失败。

 SPARK-27275:EncryptedMessage.transferTo的潜在损坏。

 SPARK-27301:DStreamCheckpointData无法清除,因为它的文件系统已缓存。

 SPARK-27338:TaskMemoryManager和UnsafeExternalSorter$SpillableIterator之间的死锁。

 SPARK-27351:AggregateEstification之后的输出行估计错误,只有空值列。

 SPARK-27390:修复包名不匹配。

 SPARK-27394:当没有任务开始或完成时,UI的过时可能会持续几分钟或几小时。

 SPARK-27403:修复updateTableStats,更新表状态时始终使用新状态或不使用任何状态。

 SPARK-27406:当两台机器的oops大小不同时,UnsafeArrayData序列化将中断。

 SPARK-27453:DSv1将自动删除DataFrameWriter.partitionBy。

 SPARK-25271:带Hive parquet的CTA应利用本地parquet源。

 SPARK-25879:选择嵌套字段和顶级字段时模式修剪失败。

 SPARK-25906:spark shell无法正确处理-i选项。

 SPARK-25921:Python worker重用导致屏障任务在没有BarrierTaskContext的情况下运行。

 SPARK-25918:LOAD DATA LOCAL INPATH应处理相对路径。

5.改进

 SPARK-27346:更新断言examples表达式中的字段信息。

 SPARK-27358:将jquery更新为1.12.X,以获取安全修补程序。

 SPARK-27479:隐藏API文档org.apache.spark.util.kvstore。

 SPARK-23523:规则OptimizeMetadataOnlyQuery导致的结果不正确。

 SPARK-23406:stream-stream self-joins的Bugs。

 SPARK-25088:rest服务器默认值和文档更新。

 SPARK-23549:比较时间戳和日期时强制转换为时间戳。

 SPARK-24324:Pandas Grouped Map UDF应按名称分配结果列。

 SPARK-23425:使用通配符加载HDFS文件路径的数据工作不正常。

 SPARK-23173:from_json可以为标记为不可为空的字段生成空值。

 SPARK-24966:为集合操作实现优先规则。

 SPARK-25708:Having没有group by的应该是全局聚合。

 SPARK-24341:正确处理子查询中的多值。

 SPARK-19724:使用现有默认位置创建托管表应引发异常。

6.任务

 SPARK-27382:更新Spark 2.4.X测试HiveExternalCatalogVersionsSuite。

 SPARK-23972:将Parquet从1.8.2更新为1.10.0。

 SPARK-25419:Parquet谓词下推改进。

 SPARK-23456:本机ORC阅读器默认为打开状态。

 SPARK-22279:默认情况下,使用本机ORC阅读器读取Hive serde表。

 SPARK-21783:默认情况下打开ORC过滤器下推。

 SPARK-24959:JSON和CSV的加速计数方法count()。

 SPARK-24244:解析csv分析器所需的列。

 SPARK-23786:csv模式验证不检查列名。

 SPARK-24423:用于指定从JDBC读取的查询的选项。

 SPARK-22814:支持JDBC分区列中的日期/时间戳。

 SPARK-24771:将avro从1.7.7更新为1.8。

 SPARK-23984:用于kubernetes的pyspark绑定。

 SPARK-24433:kubernetes的R绑定。

 SPARK-23146:支持kubernetes集群后端的客户端模式。

 SPARK-23529:支持安装kubernetes卷。

 SPARK-24215:实现对数据帧API的热切评估。

 SPARK-22274:Pandas UDF的用户定义聚合函数。

 SPARK-22239:Pandas UDF的用户定义窗口函数。

 SPARK-24396:为Python添加结构化流式ForeachWriter。

 SPARK-23874:将Apache Arrow升级到0.10.0。

 SPARK-25004:增加spark.executor.pyspark.memory限制。

 SPARK-23030:使用Arrow stream格式创建和收集Pandas DataFrames。

 SPARK-24624:支持Python UDF和scalar Pandas UDF的混合。

2.3.1 Spark SQL

Spark 2.0通过对SQL 2003的支持大幅增强了SQL功能,现在可以运行所有99个TPC-DS查询。这个版本中的SparkSQL主要有以下几点改进。

(1)引入了支持ANSISQL和HiveSQL的本地解析器。

(2)本地实现DDL命令。

(3)支持非相关标量子查询。

(4)在Where与having条件中,支持(not)in和(not)exists。

(5)即使Spark没有和Hive集成搭建,SparkSQL也支持它们一起搭建时的除了Hive连接、Hive UDF(User Defined Function用户自定义函数)和脚本转换之外的大部分功能。

(6)Hive式的分桶方式的支持。

另外,Catalyst查询优化器对于常见的工作负载也有了很多提升,对如nullability propagation之类的查询做了更好的优化。Catalyst查询优化器从最早的应用于SparkSQL到现在应用于DataSetAPI,对Spark程序的高效率运行起到了非常重要的作用,并且随着DataSetAPI的流行,以及优化器自身的不断演进,未来肯定会对Spark的所有框架带来更高的执行效率。

2.3.2 DataFrame和Dataset API

在Spark 1.X版本中,DataFrame的API存在很多问题,如DataFrame不是类型安全的(not type-safe)、不是面向对象的(not object-oriented),为了克服这些问题,Spark在1.6版本引入了Dataset,并在2.0版本的Scala和Java中将二者进行了统一(在Python和R中,由于缺少类型安全性,DataFrame仍是主要的编程接口),DataFrame成为DataSet[Row]的别名,而且Spark 2.0版本为DataSet的类型化聚合加入了一个新的聚合器,让基于DataSet的聚合更加高效。

在Spark 2.X版本中,DataFrame和Dataset API晋升为稳定的API。也就是说,可以在生产实践中使用它们,且后续会基于向后兼容的前提不断强化。

DataSetAPI是High-LevelAPI,有更高的抽象级别,与RDDAPI这样的Low-LevelAPI相比更加易用,它对于提升用户的工作效率,以及提高程序的可读性而言意义非凡。由于WholeStageCodeGeneration的引入,SparkSQL和DataSetAPI中的常见算子的性能提升了2~10倍。加上Catalyst查询优化器和Tungsten的帮助,用户不用过多地关注对程序的优化,也能获得很好的执行效率。

所以,毋庸置疑地,这样一种简单高效的API将成为Spark未来主流的编程接口。

2.3.3 Timed Window

对于经常用到复杂SQL的用户而言,窗口函数一直以来都是不可或缺的,在Spark 2.X版本中,通过对Hive中的窗口函数的本地化实现,使用Spark的内存管理机制,从而提升了窗口函数的性能。