Spark官网补缺

Spark官网补缺(2.3.4) RDD SparkSql

1.RDD

1.1文件格式

Spark的所有基于文件的输入方法(包括textFile)都支持在目录,压缩文件和通配符上运行。例如,你可以使用textFile("/my/directory"),textFile("/my/directory/*.txt")和textFile("/my/directory/*.gz")。

1.2 shell操作

在Spark Shell中,已经在名为的变量中为您创建了一个特殊的可识别解释器的SparkContext sc。制作自己的SparkContext将不起作用。您可以使用--master参数设置上下文连接到哪个主机,还可以通过将逗号分隔的列表传递给参数来将JAR添加到类路径--jars。您还可以通过在--packages参数中提供逗号分隔的Maven坐标列表,从而将依赖项(例如Spark Packages)添加到Shell会话中。可能存在依赖项的任何其他存储库(例如Sonatype)都可以传递给--repositories参数。例如,要bin/spark-shell在四个核心上运行,请使用:

$ ./bin/spark-shell --master local[4]
或者,也可以添加code.jar到其类路径中,使用:

$ ./bin/spark-shell --master local[4] --jars code.jar
要使用Maven坐标包含依赖项,请执行以下操作:

$ ./bin/spark-shell --master local[4] --packages "org.example:example:0.1"
有关选项的完整列表,请运行spark-shell --help。在后台, spark-shell调用更通用的spark-submit脚本。

1.3 惰性加载

Spark中的所有转换都是惰性的,因为它们不会立即计算出结果。相反,他们只记得应用于某些基本数据集(例如文件)的转换。仅当动作要求将结果返回给驱动程序时才计算转换。这种设计使Spark可以更高效地运行。例如,我们可以通过map创建数据集使用,reduce并且仅将reduce的结果返回给驱动程序,而不是将较大的映射数据集返回给驱动程序。

1.4 缓存(持久化)

To illustrate RDD basics, consider the simple program below:

val lines = sc.textFile("data.txt")
val lineLengths = lines.map(s => s.length)
val totalLength = lineLengths.reduce((a, b) => a + b)
第一行定义了来自外部文件的基本RDD。这个数据集没有加载到内存中,也没有在其他地方执行:行只是指向文件的指针。第二行将lineLengths定义为映射转换的结果。同样,由于懒惰,lineLengths不是立即计算的。最后,运行reduce,这是一个动作。此时,Spark将计算分解为在不同的机器上运行的任务,每台机器都运行其部分映射和局部约简,只向驱动程序返回其答案。

If we also wanted to use lineLengths again later, we could add:

lineLengths.persist()
before the reduce, which would cause lineLengths to be saved in memory after the first time it is computed.

1.5shuffle有排序吗? sorted blocks.

Internally, results from individual map tasks are kept in memory until they can’t fit. Then, these are sorted based on the target partition and written to a single file. On the reduce side, tasks read the relevant sorted blocks.
在内部,单个地图任务的结果会保留在内存中,直到无法容纳为止。然后,根据目标分区对它们进行排序并写入单个文件。在简化方面,任务读取相关的已排序块。
所述shuffle是昂贵的操作,因为它涉及的磁盘I/O,数据序列,和网络I/O。为了组织shuffle数据,Spark生成任务集-map任务以组织数据,以及一组reduce任务来聚合数据。此术语来自MapReduce(shuffle一词),与Spark map和reduce操作没有直接关系。
shuffle还会在磁盘上生成大量中间文件。从Spark 1.3开始,将保留这些文件,直到不再使用相应的RDD并进行垃圾回收为止。这样做是为了在重新计算沿袭时无需重新创建随机播放文件。如果应用程序保留了对这些RDD的引用,或者如果GC不经常启动,则垃圾收集可能仅在很长一段时间后才会发生。这意味着长时间运行的Spark作业可能会占用大量磁盘空间。spark.local.dir在配置Spark上下文时,临时存储目录由配置参数指定 。

可以通过调整各种配置参数来调整shuffle行为。请参阅《Spark配置指南》中的“Shuffle Behavior”部分

1.6调优策略之序列化

数据序列化
序列化在任何分布式应用程序的性能中都起着重要作用。将对象序列化为慢速格式或占用大量字节的格式将大大减慢计算速度。通常,这是您应该优化Spark应用程序的第一件事。Spark旨在在便利性(允许您在操作中使用任何Java类型)和性能之间取得平衡。它提供了两个序列化库:

Java序列化:默认情况下,Spark使用Java的ObjectOutputStream框架对对象进行序列化,并且可以与您创建的实现的任何类一起使用 java.io.Serializable。您还可以通过扩展来更紧密地控制序列化的性能 java.io.Externalizable。Java序列化很灵活,但是通常很慢,并且导致许多类的序列化格式很大。
Kryo序列化:Spark还可以使用Kryo库(版本2)更快地序列化对象。与Java序列化(通常多达10倍)相比,Kryo显着更快,更紧凑,但是Kryo不支持所有 Serializable类型,并且要求您预先注册要在程序中使用的类,以实现最佳性能。
您可以通过使用SparkConf初始化作业 并调用来切换为使用Kryo conf.set("spark.serializer","org.apache.spark.serializer.KryoSerializer")。

要使用Kryo注册您自己的自定义类,请使用registerKryoClasses方法。

val conf = new SparkConf().setMaster(...).setAppName(...)
conf.registerKryoClasses(Array(classOf[MyClass1], classOf[MyClass2]))
val sc = new SparkContext(conf)

1.7调优策略之内存调优

Spark中的内存使用情况大体上属于以下两类之一:执行和存储。执行内存是指用于 shuffles, joins, sorts and aggregations, 的计算的内存,而存储内存是指用于在集群中缓存和传播内部数据的内存。
。在Spark中,执行和存储共享一个统一的区域(M)。当不使用执行内存时,存储可以获取所有可用内存,反之亦然。如果有必要,执行可能会驱逐存储,但只有在总存储内存使用率下降到某个阈值(R)以下时,才可以执行该操作。换句话说,R描述了一个子区域,在该子区域M中永远不会清除缓存的块。由于实现的复杂性,存储可能无法退出执行。(暂时理解存储是可以被执行的内存请求到,从而没有存储,而执行的内存一定在的)
这种设计确保了几种理想的性能。首先,不使用缓存的应用程序可以将整个空间用于执行,从而避免了不必要的磁盘溢出。其次,确实使用缓存的应用程序可以保留最小的存储空间(R),以免其数据块被逐出。最后,这种方法可为各种工作负载提供合理的即用即用性能,而无需用户了解如何在内部划分内存。

尽管有两种相关的配置,但典型用户无需调整它们,因为默认值适用于大多数工作负载:

spark.memory.fraction表示的大小M为(JVM堆空间-300MB)的一部分(默认值为0.6)。其余空间(40%)保留用于用户数据结构,Spark中的内部元数据,并在记录稀疏和异常大的情况下防止OOM错误。
spark.memory.storageFraction将的大小表示R为的一部分M(默认为0.5)。 R是M其中的缓存块不受执行影响而退出的存储空间。
spark.memory.fraction应该设置的值,以便在JVM的旧版本或“长期使用的”一代中舒适地适应此堆空间量。有关详细信息,请参见下面有关高级GC调整的讨论。

1.7.1数据结构

调整数据结构
减少内存消耗的第一种方法是避免增加开销的Java特性,比如基于指针的数据结构和包装器对象。有几种方法可以做到这一点:
设计您的数据结构以选择对象数组和基本类型,而不是标准的Java或Scala集合类(例如HashMap)。fastutil库为与Java标准库兼容的基本类型提供了方便的集合类。
尽可能避免嵌套有大量小对象和指针的结构。
考虑使用数字id或枚举对象代替键的字符串。
如果您的内存不足32 GB,可以设置JVM标志-XX:+UseCompressedOops,使指针为4字节,而不是8字节。您可以在spark-env.sh中添加这些选项。

1.7.2序列化

当您的对象仍然太大而无法进行优化存储时,减少内存使用的一种更简单的方法是使用RDD持久性API中的序列化StorageLevels (例如)以序列化形式存储它们。然后,Spark将每个RDD分区存储为一个大字节数组。由于必须动态地反序列化每个对象,因此以串行形式存储数据的唯一缺点是访问时间较慢。如果您想以序列化形式缓存数据,我们强烈建议使用Kryo,因为它导致的大小比Java序列化(当然也比原始Java对象)小。MEMORY_ONLY_SER

1.7.3gc优化

当您的程序存储了大量的RDDs时,JVM垃圾收集可能会成为一个问题。(对于那些只读取一次RDD,然后在上面运行许多操作的程序来说,这通常不是问题。)当Java需要清除旧对象以为新对象腾出空间时,它将需要跟踪所有Java对象并找到未使用的对象。这里要记住的要点是,垃圾收集的成本与Java对象的数量成正比,因此使用对象较少的数据结构(例如,一个Ints数组而不是一个LinkedList)可以大大降低此成本。
衡量GC的影响

GC调整的第一步是收集有关垃圾收集发生频率和花费GC时间的统计信息。这可以通过添加-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStampsJava选项来完成。(有关将Java选项传递给Spark作业的信息,请参阅配置指南。)下次运行Spark作业时,每次发生垃圾收集时,您都会在工作日志中看到打印的消息。请注意,这些日志将位于群集的工作节点上(stdout位于其工作目录中的文件中),而不是驱动程序上。

高级GC调整

为了进一步调整垃圾回收,我们首先需要了解有关JVM中内存管理的一些基本信息:

Java Heap空间分为Young和Old两个区域。年轻一代用于保存寿命短的对象,而老一代则用于寿命更长的对象。

年轻一代又分为三个区域[Eden,Survivor1,Survivor2]。

垃圾收集过程的简化描述:当Eden已满时,将在Eden上运行次要GC,并将来自Eden和Survivor1的活动对象复制到Survivor2。幸存者区域被交换。如果对象足够旧或Survivor2已满,则将其移到“旧”。最后,当Old接近满时,将调用完整的GC。

在Spark中进行GC调整的目标是确保在旧一代中仅存储长寿命的RDD,并确保新世代具有足够的大小以存储短寿命的对象。这将有助于避免完整的GC收集任务执行期间创建的临时对象。可能有用的一些步骤是:
通过收集GC统计信息来检查是否有太多的垃圾收集。如果在任务完成之前多次调用full GC,这意味着没有足够的可用内存来执行任务。
如果minor collections but not many major GCs,那么为Eden分配更多的内存会有所帮助。您可以将Eden的大小设置为高估每个任务所需的内存大小。如果伊甸园的大小被确定为E,那么您可以使用选项-Xmn=4/3*E设置年轻代的大小。(4/3的比例也考虑到了幸存者区域使用的空间。)
在打印的GC统计数据中,如果oldGEN接近满,通过降低spark.memory.fraction来减少用于缓存的内存量;缓存更少的对象比降低任务执行速度要好。或者,考虑减少年轻一代的规模。这意味着降低-Xmn,如果你把它设为上面的值。如果没有,尝试更改JVM的NewRatio参数的值。许多jvm将其默认为2,这意味着老一代占用了2/3的堆。它应该足够大以至于这个分数超过火花。记忆。分数。
尝试使用-XX:+UseG1GC的G1GC垃圾收集器。在垃圾收集成为瓶颈的某些情况下,它可以提高性能。注意,对于较大的执行程序堆大小,使用-XX:G1HeapRegionSize来增加G1区域大小可能很重要
如果您的任务是从HDFS读取数据,则可以使用从HDFS读取的数据块的大小来估算任务使用的内存量。注意,解压缩块的大小通常是块大小的2或3倍。因此,如果我们希望拥有3或4个任务的工作空间,并且HDFS块大小为128 MB,则我们可以估计Eden的大小为4 * 3 * 128MB。

监视垃圾收集所花费的频率和时间随新设置的变化。

1.8调优的其他配置

并行度
除非将每个操作的并行度设置得足够高,否则集群不会得到充分利用。Spark根据每个文件的大小自动设置要在每个文件上运行的map任务的数量(尽管您可以通过SparkContext的可选参数来控制它。对于分布式reduce操作,例如groupByKey和reduceByKey,它使用最大的父RDD的分区数。您可以将并行度级别作为第二个参数传递(参见spark),或者设置config属性spark.default.parallelism来更改默认值。通常,我们建议在您的集群中每个CPU内核执行2-3个任务。


减少reduce任务的内存使用
有时,您会收到OutOfMemoryError的原因不是因为您的RDD不能容纳在内存中,而是因为您的一项任务(例如中的reduce任务之一)的工作集groupByKey太大。spark的reduce操作(sortByKey,groupByKey,reduceByKey,join,等)建立每个任务中的哈希表来进行分组,而这往往是大的。此处最简单的解决方法是 提高并行度,以使每个任务的输入集更小。Spark可以高效地支持短至200 ms的任务,因为它可以在多个任务中重用一个执行器JVM,并且任务启动成本低,因此您可以安全地将并行级别提高到集群中核心的数量以上。

广播大变量
使用SparkContext中可用的广播功能可以极大地减少每个序列化任务的大小,以及通过集群启动作业的成本。如果您的任务使用了驱动程序中的任何大对象(例如,一个静态查找表),请考虑将其转换为广播变量。Spark在主服务器上打印每个任务的序列化大小,因此您可以查看它来确定任务是否太大;一般来说,大于20kb的任务可能值得优化。

数据局部性
数据局部性可能会对Spark作业的性能产生重大影响。如果数据和对其进行操作的代码在一起,则计算速度往往会很快。但是,如果代码和数据是分开的,那么一个必须移到另一个。通常,从一个地方到另一个地方传送序列化代码要比块数据更快,因为代码大小比数据小得多。Spark围绕此数据本地性一般原则构建调度。

数据局部性是数据与处理它的代码之间的接近程度。根据数据的当前位置,可分为多个级别。从最远到最远的顺序:

PROCESS_LOCAL数据与正在运行的代码位于同一JVM中。这是最好的位置
NODE_LOCAL数据在同一节点上。示例可能在同一节点上的HDFS中,或者在同一节点上的另一执行程序中。这比PROCESS_LOCAL由于数据必须在进程之间传输而要慢一些
NO_PREF 可以从任何地方快速访问数据,并且不受位置限制
RACK_LOCAL数据位于同一服务器机架上。数据位于同一机架上的其他服务器上,因此通常需要通过单个交换机通过网络发送
ANY 数据在网络上的其他位置,而不是在同一机架中
Spark倾向于在最佳位置级别安排所有任务,但这并不总是可能的。在任何空闲执行器上没有未处理数据的情况下,Spark会切换到较低的本地级别。有两种选择:a)等待忙碌的CPU释放以在同一服务器上的数据上启动任务,或b)立即在需要将数据移动到更远的地方启动新任务。

Spark通常要做的是稍等一下,以期释放繁忙的CPU。一旦超时到期,它将开始将数据从很远的地方移到空闲的CPU中。每个级别之间的回退等待超时可以单独配置,也可以一起配置在一个参数中。有关详细信息,请参见配置页面spark.locality上的 参数。如果您的任务很长并且位置不佳,则应该增加这些设置,但是默认设置通常效果很好。

2.Spark Sql

2.1.1全局临时视图

Spark SQL中的临时视图是会话作用域的,如果创建它的会话终止,它将消失。如果要在所有会话之间共享一个临时视图并保持活动状态,直到Spark应用程序终止,则可以创建全局临时视图。全局临时视图与系统保留的数据库相关联global_temp,我们必须使用限定名称来引用它,例如SELECT * FROM global_temp.view1

2.2.1spark dataframe类型简介

Spark SQL是一个用于结构化数据处理的Spark模块。与基本的Spark RDD API不同,Spark SQL提供的接口为Spark提供了有关数据结构和正在执行的计算的更多信息。在内部,Spark SQL使用这些额外的信息来执行额外的优化。有几种与Spark SQL交互的方法,包括SQL和Dataset API。当计算结果时,使用相同的执行引擎,而不依赖于使用哪种API/语言来表示计算。这种统一意味着开发人员可以很容易地在不同的api之间来回切换,而这些api提供了表达给定转换的最自然的方式。本页上的所有示例都使用Spark发行版中包含的示例数据,可以在Spark -shell、pyspark shell或sparkR shell中运行。
schema:
Spark SQL提供的接口为Spark提供了有关数据结构和正在执行的计算的更多信息。在内部,Spark SQL使用这些额外的信息来执行额外的优化。

2.2.2Dataframe和DataSet的row类型

DataFrames provide a domain-specific language for structured data manipulation in Scala, Java, Python and R. As mentioned above, in Spark 2.0, DataFrames are just Dataset of Rows in Scala and Java API. These operations are also referred as “untyped transformations” in contrast to “typed transformations” come with strongly typed Scala/Java Datasets. Here we include some basic examples of structured data processing using Datasets:
中文(简体)
DataFrame为Scala,Java,Python和R中的结构化数据操作提供了一种特定于域的语言。 如上所述,在Spark 2.0中,DataFrame只是Scala和Java API中的row的DATASET。与强类型的Scala / Java数据集附带的“类型转换”相反,这些操作也称为“非类型转换”。 这里我们包括一些使用数据集进行结构化数据处理的基本示例:

2.2.3数据集的序列化

Datasets are similar to RDDs, however, instead of using Java serialization or Kryo they use a specialized Encoder to serialize the objects for processing or transmitting over the network. While both encoders and standard serialization are responsible for turning an object into bytes, encoders are code generated dynamically and use a format that allows Spark to perform many operations like filtering, sorting and hashing without deserializing the bytes back into an object.

数据集类似于RDD,但是,它们不使用Java序列化或Kryo,而是使用专用的Encoder对对象进行序列化以进行处理或通过网络传输。虽然编码器和标准序列化都负责将对象转换为字节,但是编码器是动态生成的代码,并使用一种格式,该格式允许Spark执行许多操作,如过滤,排序和哈希处理,而无需将字节反序列化为对象。

2.2.4与RDD的互操作

Spark SQL支持两种将现有RDD转换为数据集的方法。第一种方法使用反射来推断包含特定对象类型的RDD的架构。这种基于反射的方法可以使代码更简洁,当您在编写Spark应用程序时已经了解架构时,可以很好地工作。

创建数据集的第二种方法是通过编程界面,该界面允许您构造模式,然后将其应用于现有的RDD。尽管此方法较为冗长,但可以在运行时才知道列及其类型的情况下构造数据集。
// For implicit conversions from RDDs to DataFrames
import spark.implicits._

// Create an RDD of Person objects from a text file, convert it to a Dataframe
val peopleDF = spark.sparkContext
  .textFile("examples/src/main/resources/people.txt")
  .map(_.split(","))
  .map(attributes => Person(attributes(0), attributes(1).trim.toInt))
  .toDF()
// Register the DataFrame as a temporary view
peopleDF.createOrReplaceTempView("people")

// SQL statements can be run by using the sql methods provided by Spark
val teenagersDF = spark.sql("SELECT name, age FROM people WHERE age BETWEEN 13 AND 19")

// The columns of a row in the result can be accessed by field index
teenagersDF.map(teenager => "Name: " + teenager(0)).show()
// +------------+
// |       value|
// +------------+
// |Name: Justin|
// +------------+

// or by field name
teenagersDF.map(teenager => "Name: " + teenager.getAs[String]("name")).show()
// +------------+
// |       value|
// +------------+
// |Name: Justin|
// +------------+

// No pre-defined encoders for Dataset[Map[K,V]], define explicitly
implicit val mapEncoder = org.apache.spark.sql.Encoders.kryo[Map[String, Any]]
// Primitive types and case classes can be also defined as
// implicit val stringIntMapEncoder: Encoder[Map[String, Any]] = ExpressionEncoder()

// row.getValuesMap[T] retrieves multiple columns at once into a Map[String, T]
teenagersDF.map(teenager => teenager.getValuesMap[Any](List("name", "age"))).collect()
// Array(Map("name" -> "Justin", "age" -> 19))

import org.apache.spark.sql.types._

// Create an RDD
val peopleRDD = spark.sparkContext.textFile("examples/src/main/resources/people.txt")

// The schema is encoded in a string
val schemaString = "name age"

// Generate the schema based on the string of schema
val fields = schemaString.split(" ")
  .map(fieldName => StructField(fieldName, StringType, nullable = true))
val schema = StructType(fields)

// Convert records of the RDD (people) to Rows
val rowRDD = peopleRDD
  .map(_.split(","))
  .map(attributes => Row(attributes(0), attributes(1).trim))

// Apply the schema to the RDD
val peopleDF = spark.createDataFrame(rowRDD, schema)

// Creates a temporary view using the DataFrame
peopleDF.createOrReplaceTempView("people")

// SQL can be run over a temporary view created using DataFrames
val results = spark.sql("SELECT name FROM people")

// The results of SQL queries are DataFrames and support all the normal RDD operations
// The columns of a row in the result can be accessed by field index or by field name
results.map(attributes => "Name: " + attributes(0)).show()
// +-------------+
// |        value|
// +-------------+
// |Name: Michael|
// |   Name: Andy|
// | Name: Justin|
// +-------------+

2.2.5spark的隐式转换

1、toDF的隐式转换

import spark.implicits._

2、spark.read.kudu中的kudu的隐式转换

improt org.apache.kudu.spark.kudu._
#spark指的是代码中创建的对象

3.asjava的隐式转换
import scala.collection.JavaConverters._

4.通过schema构建df时,设置StructField的Type
import org.apache.spark.sql.types._

5.定义map[String,Any]的反序列化格式kryo
implicit val mapEncoder = org.apache.spark.sql.Encoders.kryo[Map[String, Any]]

2.2.6用户自定义的聚合函数(UDAF)

这里不做介绍

2.2.7数据源

数据源
Spark SQL支持通过DataFrame接口对各种数据源进行操作。DataFrame可以使用关系转换进行操作,也可以用于创建临时视图。将DataFrame注册为临时视图使您可以对其数据运行SQL查询。本节介绍了使用Spark数据源加载和保存数据的一般方法,然后介绍了可用于内置数据源的特定选项
以最简单的形式,所有操作都将使用默认数据源(parquet除非另有配置 spark.sql.sources.default)。
实例:默认parquet的格式
val usersDF = spark.read.load("examples/src/main/resources/users.parquet")
usersDF.select("name", "favorite_color").write.save("namesAndFavColors.parquet")
手动指定选项
您还可以手动指定将要使用的数据源以及要传递给数据源的任何其他选项。数据源通过其全名指定(即org.apache.spark.sql.parquet),但内置的来源,你也可以使用自己的短名称(json,parquet,jdbc,orc,libsvm,csv,text)。从任何数据源类型加载的DataFrame都可以使用此语法转换为其他类型。
val peopleDF = spark.read.format("json").load("examples/src/main/resources/people.json")
peopleDF.select("name", "age").write.format("parquet").save("namesAndAges.parquet")
要加载JSON文件,您可以使用:
option
val peopleDFCsv = spark.read.format("csv")
  .option("sep", ";")
  .option("inferSchema", "true")
  .option("header", "true")
  .load("examples/src/main/resources/people.csv")

2.2.8savemode

保存操作可以选择带SaveMode,指定如何处理现有数据(如果存在)。重要的是要认识到这些保存模式不利用任何锁定,也不是原子的。另外,执行时Overwrite,将在写出新数据之前删除数据。

Scala / Java任何语言含义
SaveMode.ErrorIfExists (默认)"error" or "errorifexists" (默认)将DataFrame保存到数据源时,如果已经存在数据,则将引发异常。
SaveMode.Append"append"将DataFrame保存到数据源时,如果已经存在数据/表,则应该将DataFrame的内容附加到现有数据中。
SaveMode.Overwrite"overwrite"覆盖模式意味着将DataFrame保存到数据源时,如果已经存在数据/表,则预期现有数据将被DataFrame的内容覆盖。
SaveMode.Ignore"ignore"忽略模式意味着在将DataFrame保存到数据源时,如果已经存在数据,则预期保存操作不会保存DataFrame的内容并且不会更改现有数据。这类似于CREATE TABLE IF NOT EXISTSSQL中的。

2.2.9saveAsTable

`DataFrames`也可以使用以下`saveAsTable` 命令作为持久表保存到Hive Metastore中。请注意,使用此功能不需要现有的Hive部署。Spark将为您创建一个默认的本地Hive Metastore(使用Derby)。与`createOrReplaceTempView`命令不同, `saveAsTable`它将具体化DataFrame的内容并在Hive元存储中创建一个指向数据的指针。即使您重新启动Spark程序,持久表仍将存在,只要您保持与同一metastore的连接即可。可以通过使用表名称`table`在上调用方法来创建持久表的DataFrame `SparkSession`。
对于基于文件的数据源,例如文本,镶木地板,json等),您可以通过path选项指定自定义表格路径,
例如df.write.option(“ path”,“ /some/path").saveAsTable("t”)。
删除表后,自定义表路径将不会删除,并且表数据仍然存在。如果未指定自定义表路径,Spark会将数据写入仓库目录下的默认表路径。删除表后,默认表路径也将被删除
2.2.9.1save时指定分区\分桶\排序
peopleDF.write.bucketBy(42, "name").sortBy("age").saveAsTable("people_bucketed")
usersDF.write.partitionBy("favorite_color").format("parquet").save("namesPartByColor.parquet")
可以对单个表使用分区和存储桶:
usersDF
  .write
  .partitionBy("favorite_color")
  .bucketBy(42, "name")
  .saveAsTable("users_partitioned_bucketed")
  partitionBy创建一个目录结构,如分区发现部分所述。因此,它对具有高基数的列的适用性有限。相反,bucketBy将数据分布在固定数量的桶上,并且可以在许多惟一值是无界的情况下使用。
2.2.9.2read / write parquet
parquet是一种柱状格式,得到许多其他数据处理系统的支持。Spark SQL支持读写拼花文件,自动保存原始数据的模式。在编写parquet文件时,出于兼容性的考虑,所有列都会自动转换为可空。

2.3.0表分区

表分区是Hive等系统中常用的优化方法。在分区表中,数据通常存储在不同的目录中,分区列值编码在每个分区目录的路径中。所有内置的文件源(包括文本/CSV/JSON/ORC/Parquet)都能够自动发现和推断分区信息。例如,我们可以使用以下目录结构将以前使用的所有人口数据存储到分区表中,并添加两个额外的列,性别和国家作为分区列:

通过传递path/to/table给SparkSession.read.parquet或SparkSession.read.load,Spark SQL将自动从路径中提取分区信息。现在,返回的DataFrame的架构变为:
与ProtocolBuffer、Avro和Thrift一样,Parquet也支持模式演化。用户可以从一个简单的模式开始,然后根据需要逐步向模式中添加更多的列。这样,用户可能会得到多个模式不同但相互兼容的parquet文件。Parquet数据源现在能够自动检测这种情况并合并所有这些文件的模式。
由于模式合并是一个相对昂贵的操作,并且在大多数情况下不是必需的,所以我们从1.5.0开始默认关闭它。你可以使它成为可能
读取parquet文件时将数据源选项mergeSchema设置为true(如下面的示例所示),或者
设置全局SQL选项spark.sql.parquet.mergeSchema为true。
// This is used to implicitly convert an RDD to a DataFrame.
import spark.implicits._

// Create a simple DataFrame, store into a partition directory
val squaresDF = spark.sparkContext.makeRDD(1 to 5).map(i => (i, i * i)).toDF("value", "square")
squaresDF.write.parquet("data/test_table/key=1")

// Create another DataFrame in a new partition directory,
// adding a new column and dropping an existing column
val cubesDF = spark.sparkContext.makeRDD(6 to 10).map(i => (i, i * i * i)).toDF("value", "cube")
cubesDF.write.parquet("data/test_table/key=2")

// Read the partitioned table
val mergedDF = spark.read.option("mergeSchema", "true").parquet("data/test_table")
mergedDF.printSchema()

// The final schema consists of all 3 columns in the Parquet files together
// with the partitioning column appeared in the partition directory paths
// root
//  |-- value: int (nullable = true)
//  |-- square: int (nullable = true)
//  |-- cube: int (nullable = true)
//  |-- key: int (nullable = true)

|value|square|cube|key|
+-----+------+----+---+
|    3|     9|null|  1|
|    4|    16|null|  1|
|    5|    25|null|  1|
|    8|  null| 512|  2|
|    9|  null| 729|  2|
|   10|  null|1000|  2|
|    1|     1|null|  1|
|    2|     4|null|  1|
|    6|  null| 216|  2|
|    7|  null| 343|  2|
+-----+------+----+---+

2.3.1 hive metastore Parquet table 转换

Hive metastore Parquet table conversion
当读取和写入Hive metastore Parquet时,Spark SQL将尝试使用自己的 Parquet支持,而不是Hive SerDe,以获得更好的性能。此行为由spark.sql.hive控制。转换spark.sql.hive.convertMetastoreParquet配置,默认为true。
从表模式处理的角度来看,Hive和Parquet之间有两个关键区别。
Hive不区分大小写,而Parquet不区分大小写
Hive认为所有列都可为空,而Parquet中的可为空性很重要
因此,在将一个Hive metastore Parquet table转换成一个Spark SQL Parquet table时,我们必须协调Hive metastore schema和Parquet schema。
和解规则如下:
无论是否为空,两个模式中具有相同名称的字段必须具有相同的数据类型。调整后的字段应该具有Parquet side的数据类型,以保证可为空。
协调模式包含在Hive metastore模式中定义的字段。
只出现在parquet模式中的任何字段都将在协调模式中删除。
任何只出现在Hive metastore模式中的字段都会被添加到和解模式中作为可空字段。

2.3.2元数据刷新

Spark SQL缓存Parquet元数据以获得更好的性能。启用Hive Metastore Parquet表转换后,这些转换表的元数据也会被缓存。如果这些表是通过Hive或其他外部工具更新的,则需要手动刷新它们以确保元数据一致。

// spark is an existing SparkSession
spark.catalog.refreshTable("my_table")

2.3.3 Configuration

可以使用setConfon中的方法SparkSessionSET key=value使用SQL 运行 命令来完成Parquet的配置。

物业名称默认含义
spark.sql.parquet.binaryAsStringfalse编写Parquet模式时,其他一些Parquet产生系统,尤其是Impala,Hive和旧版本的Spark SQL,不会区分二进制数据和字符串。该标志告诉Spark SQL将二进制数据解释为字符串,以提供与这些系统的兼容性。
spark.sql.parquet.int96AsTimestamptrue一些Parquet-producing系统,特别是Impala和Hive,将时间戳存储在INT96中。此标志告诉Spark SQL将INT96数据解释为时间戳,以提供与这些系统的兼容性。
spark.sql.parquet.compression.codecsnappy设置编写Parquet文件时使用的压缩编解码器。如果在特定于表的选项/属性中指定了“压缩”或“ parquet.compression”,则优先级为“压缩”,“ parquet.compression”,“ spark.sql.parquet.compression.codec”。可接受的值包括:none,uncompressed,snappy,gzip,lzo。
spark.sql.parquet.filterPushdowntrue设置为true时启用Parquet过滤器下push-down 优化。
spark.sql.hive.convertMetastoreParquettrue设置为false时,Spark SQL将使用Hive SerDe用于Parquet表,而不是内置支持。
spark.sql.parquet.mergeSchemafalse设置为true时,Parquet数据源合并从所有数据文件收集的架构,否则从摘要文件或随机数据文件(如果没有摘要文件可用)中选择架构。

2.3.4 ORC文件

从Spark 2.3开始,Spark就支持一个带新ORC文件格式的矢量化ORC读取器。为此,新添加了以下配置。当spark.sql.orc时,向量化的读取器用于本地ORC表(例如,使用ORC子句创建的表)。impl被设置为native和spark.sql.orc。enableVectorizedReader设置为true。对于Hive ORC serde表(例如,使用Hive选项子句USING HIVE OPTIONS (fileFormat 'ORC'),向量化的读取器在spark.sql.hive时使用。convertMetastoreOrc也被设置为true。

物业名称默认含义
spark.sql.orc.implhiveORC实现的名称。可以是native和之一hivenative表示基于Apache ORC 1.4.1构建的本机ORC支持。“ hive”是指Hive 1.2.1中的ORC库。
spark.sql.orc.enableVectorizedReadertruenative实现中启用向量化orc解码。如果为false,则在native实现中使用新的非矢量化ORC读取器。为了hive实现,这被忽略。

2.3.5 JSON数据集

Spark SQL可以自动推断JSON数据集的架构并将其作为加载Dataset[Row]。这种转换是可以做到用SparkSession.read.json()在任一Dataset[String]或JSON文件。

请注意,以json文件形式提供的文件不是典型的JSON文件。每行必须包含一个单独的,自包含的有效JSON对象。有关更多信息,请参见 JSON Lines文本格式,也称为newline分隔的JSON

对于常规的多行JSON文件,请将multiLine选项设置为true

// Primitive types (Int, String, etc) and Product types (case classes) encoders are
// supported by importing this when creating a Dataset.
import spark.implicits._

// A JSON dataset is pointed to by path.
// The path can be either a single text file or a directory storing text files
val path = "examples/src/main/resources/people.json"
val peopleDF = spark.read.json(path)

// The inferred schema can be visualized using the printSchema() method
peopleDF.printSchema()
// root
//  |-- age: long (nullable = true)
//  |-- name: string (nullable = true)

// Creates a temporary view using the DataFrame
peopleDF.createOrReplaceTempView("people")

// SQL statements can be run by using the sql methods provided by spark
val teenagerNamesDF = spark.sql("SELECT name FROM people WHERE age BETWEEN 13 AND 19")
teenagerNamesDF.show()
// +------+
// |  name|
// +------+
// |Justin|
// +------+

// Alternatively, a DataFrame can be created for a JSON dataset represented by
// a Dataset[String] storing one JSON object per string
val otherPeopleDataset = spark.createDataset(
  """{"name":"Yin","address":{"city":"Columbus","state":"Ohio"}}""" :: Nil)
val otherPeople = spark.read.json(otherPeopleDataset)
otherPeople.show()
// +---------------+----+
// |        address|name|
// +---------------+----+
// |[Columbus,Ohio]| Yin|
// +---------------+----+

2.3.6 hive table

Spark SQL还支持读写存储在Apache Hive中的数据。但是,由于Hive具有大量依赖关系,因此默认的Spark分发中不包含这些依赖关系。如果可以在类路径上找到Hive依赖项,Spark将自动加载它们。请注意,这些Hive依赖项也必须存在于所有工作节点上,因为它们将需要访问Hive序列化和反序列化库(SerDes)才能访问存储在Hive中的数据。

通过将hive-site.xml,core-site.xml(对于安全性配置)和hdfs-site.xml(对于HDFS配置)文件放置在中来配置Hive conf/。

使用Hive时,必须实例化SparkSessionHive支持,包括与持久性Hive元存储库的连接,对Hive Serdes的支持以及Hive用户定义的功能。没有现有Hive部署的用户仍可以启用Hive支持。如果未由配置hive-site.xml,则上下文会自动metastore_db在当前目录中创建并创建由配置spark.sql.warehouse.dir的目录spark-warehouse,该目录默认 为启动Spark应用程序的当前目录中的目录。请注意,自Spark 2.0.0起不推荐使用hive.metastore.warehouse.dirin 的属性hive-site.xml。而是使用spark.sql.warehouse.dir指定仓库中数据库的默认位置。您可能需要向启动Spark应用程序的用户授予写权限。
import java.io.File

import org.apache.spark.sql.{Row, SaveMode, SparkSession}

case class Record(key: Int, value: String)

// warehouseLocation points to the default location for managed databases and tables
val warehouseLocation = new File("spark-warehouse").getAbsolutePath

val spark = SparkSession
  .builder()
  .appName("Spark Hive Example")
  .config("spark.sql.warehouse.dir", warehouseLocation)
  .enableHiveSupport()
  .getOrCreate()

import spark.implicits._
import spark.sql

sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING) USING hive")
sql("LOAD DATA LOCAL INPATH 'examples/src/main/resources/kv1.txt' INTO TABLE src")

// Queries are expressed in HiveQL
sql("SELECT * FROM src").show()
// +---+-------+
// |key|  value|
// +---+-------+
// |238|val_238|
// | 86| val_86|
// |311|val_311|
// ...

// Aggregation queries are also supported.
sql("SELECT COUNT(*) FROM src").show()
// +--------+
// |count(1)|
// +--------+
// |    500 |
// +--------+

// The results of SQL queries are themselves DataFrames and support all normal functions.
val sqlDF = sql("SELECT key, value FROM src WHERE key < 10 ORDER BY key")

// The items in DataFrames are of type Row, which allows you to access each column by ordinal.
val stringsDS = sqlDF.map {
  case Row(key: Int, value: String) => s"Key: $key, Value: $value"
}
stringsDS.show()
// +--------------------+
// |               value|
// +--------------------+
// |Key: 0, Value: val_0|
// |Key: 0, Value: val_0|
// |Key: 0, Value: val_0|
// ...

// You can also use DataFrames to create temporary views within a SparkSession.
val recordsDF = spark.createDataFrame((1 to 100).map(i => Record(i, s"val_$i")))
recordsDF.createOrReplaceTempView("records")

// Queries can then join DataFrame data with data stored in Hive.
sql("SELECT * FROM records r JOIN src s ON r.key = s.key").show()
// +---+------+---+------+
// |key| value|key| value|
// +---+------+---+------+
// |  2| val_2|  2| val_2|
// |  4| val_4|  4| val_4|
// |  5| val_5|  5| val_5|
// ...

// Create a Hive managed Parquet table, with HQL syntax instead of the Spark SQL native syntax
// `USING hive`
sql("CREATE TABLE hive_records(key int, value string) STORED AS PARQUET")
// Save DataFrame to the Hive managed table
val df = spark.table("src")
df.write.mode(SaveMode.Overwrite).saveAsTable("hive_records")
// After insertion, the Hive managed table has data now
sql("SELECT * FROM hive_records").show()
// +---+-------+
// |key|  value|
// +---+-------+
// |238|val_238|
// | 86| val_86|
// |311|val_311|
// ...

// Prepare a Parquet data directory
val dataDir = "/tmp/parquet_data"
spark.range(10).write.parquet(dataDir)
// Create a Hive external Parquet table
sql(s"CREATE EXTERNAL TABLE hive_bigints(id bigint) STORED AS PARQUET LOCATION '$dataDir'")
// The Hive external table should already have data
sql("SELECT * FROM hive_bigints").show()
// +---+
// | id|
// +---+
// |  0|
// |  1|
// |  2|
// ... Order may vary, as spark processes the partitions in parallel.

// Turn on flag for Hive Dynamic Partitioning
spark.sqlContext.setConf("hive.exec.dynamic.partition", "true")
spark.sqlContext.setConf("hive.exec.dynamic.partition.mode", "nonstrict")
// Create a Hive partitioned table using DataFrame API
df.write.partitionBy("key").format("hive").saveAsTable("hive_part_tbl")
// Partitioned column `key` will be moved to the end of the schema.
sql("SELECT * FROM hive_part_tbl").show()
// +-------+---+
// |  value|key|
// +-------+---+
// |val_238|238|
// | val_86| 86|
// |val_311|311|
// ...

spark.stop()

2.3.7 hive的存储格式

请注意,创建表时尚不支持Hive存储处理程序,您可以在Hive端使用存储处理程序创建表,并使用Spark SQL读取表。

创建Hive表时,需要定义此表应如何从文件系统读取/写入数据,即“输入格式”和“输出格式”。您还需要定义该表应如何将数据反序列化为行,或将行序列化为数据,即“ serde”。以下选项可用于指定存储格式(“ serde”,“ input format”,“ output format”),例如CREATE TABLE src(id int) USING hive OPTIONS(fileFormat 'parquet')。默认情况下,我们将以纯文本形式读取表文件。请注意,创建表时尚不支持Hive存储处理程序,您可以在Hive端使用存储处理程序创建表,并使用Spark SQL读取表。
field含义
fileFormatfileFormat是一种存储格式规范的软件包,其中包括“ serde”,“ input format”和“ output format”。目前,我们支持6种文件格式:“ sequencefile”,“ rcfile”,“ orc”,“ parquet”,“ textfile”和“ avro”。
inputFormat, outputFormat这2个选项将相应的InputFormat和OutputFormat类的名称指定为字符串文字,例如org.apache.hadoop.hive.ql.io.orc.OrcInputFormat。这两个选项必须成对出现,如果已经指定了fileFormat选项,则不能指定它们。
serde此选项指定Serde类的名称。当指定fileFormat选项时,如果给定的fileFormat已经包含serde信息,则不要指定此选项。当前,“ sequencefile”,“ textfile”和“ rcfile”不包括SERDE信息,您可以将此选项与这三种文件格式一起使用。
fieldDelim, escapeDelim, collectionDelim, mapkeyDelim, lineDelim这些选项只能与“文本文件” fileFormat一起使用。它们定义了如何将定界文件读取为行。

定义的所有其他属性OPTIONS将被视为Hive serde属性。

2.3.8 与Hive Metastore的不同版本进行交互

请注意,与用于与metastore进行通信的Hive版本无关,Spark SQL在内部将针对Hive 1.2.1进行编译

与Hive metastore的交互是Spark SQL对Hive的最重要支持之一,它使Spark SQL能够访问Hive表的元数据。从Spark 1.4.0开始,使用以下描述的配置,可以使用Spark SQL的单个二进制版本来查询Hive元存储库的不同版本。请注意,与用于与metastore进行通信的Hive版本无关,Spark SQL在内部将针对Hive 1.2.1进行编译,并将这些类用于内部执行(serdes,UDF,UDAF等)。

可以使用以下选项来配置用于检索元数据的Hive版本:
field默认含义
spark.sql.hive.metastore.version1.2.1Hive Metastore的版本。可用的选项是0.12.0通过1.2.1
spark.sql.hive.metastore.jarsbuiltin用于实例化HiveMetastoreClient的jar的位置。此属性可以是三个选项之一:builtin使用Hive 1.2.1,该模块在-Phive启用时与Spark组件捆绑在一起。选择此选项时,spark.sql.hive.metastore.version必须1.2.1定义或不定义。maven使用从Maven存储库下载的指定版本的Hive jar。通常不建议将此配置用于生产部署。JVM标准格式的类路径。这个类路径必须包含所有的Hive及其依赖项,包括正确的Hadoop版本。这些jar只需要出现在驱动程序上,但是如果您在yarn集群模式下运行,那么您必须确保它们与您的应用程序打包在一起。
spark.sql.hive.metastore.sharedPrefixescom.mysql.jdbc,org.postgresql,com.microsoft.sqlserver,oracle.jdbc逗号分隔的类前缀列表,应使用在Spark SQL和特定版本的Hive之间共享的类加载器加载。应该共享的类的一个示例是与元存储区对话所需的JDBC驱动程序。需要共享的其他类是与已经共享的类进行交互的类。例如,log4j使用的自定义追加程序。
spark.sql.hive.metastore.barrierPrefixes(empty)以逗号分隔的类前缀列表,应为Spark SQL与之通信的每个Hive版本显式重新加载。例如,在通常会被共享的前缀中声明的Hive UDF(即org.apache.spark.*)。

2.3.9 JDBC

物业名称含义
url要连接的JDBC URL。特定于源的连接属性可以在URL中指定。例如,jdbc:postgresql://localhost/test?user=fred&password=secret
dbtable应该读取的JDBC表。请注意,FROM可以使用在SQL查询子句中有效的任何内容。例如,除了完整表之外,您还可以在括号中使用子查询。
driver用于连接到该URL的JDBC驱动程序的类名。
partitionColumn, lowerBound, upperBound如果指定了这些选项,则必须全部指定。另外, numPartitions必须指定。它们描述了从多个工作程序并行读取时如何对表进行分区。 partitionColumn必须是相关表格中的数字列。请注意,lowerBoundupperBound仅用于确定分区的步幅,而不是用于过滤表中的行。因此,表中的所有行都将被分区并返回。此选项仅适用于阅读。
numPartitions表读写中可用于并行处理的最大分区数。这也确定了并发JDBC连接的最大数量。如果要写入的分区数超过此限制,我们可以通过coalesce(numPartitions)在写入之前进行调用将其降低到此限制。
fetchsizeJDBC的获取大小,它确定每次往返要获取多少行。这可以帮助提高JDBC驱动程序的性能,该驱动程序默认为较小的访存大小(例如,具有10行的Oracle)。此选项仅适用于阅读。
batchsizeJDBC批处理大小,它确定每次往返要插入多少行。这可以帮助提高JDBC驱动程序的性能。此选项仅适用于写作。默认为1000
isolationLevel事务隔离级别,适用于当前连接。它可以是一个NONEREAD_COMMITTEDREAD_UNCOMMITTEDREPEATABLE_READ,或SERIALIZABLE,对应于由JDBC的连接对象定义,缺省值为标准事务隔离级别READ_UNCOMMITTED。此选项仅适用于写作。请参阅中的文档java.sql.Connection
sessionInitStatement在向远程数据库打开每个数据库会话之后且开始读取数据之前,此选项将执行自定义SQL语句(或PL / SQL块)。使用它来实现会话初始化代码。例:option("sessionInitStatement", """BEGIN execute immediate 'alter session set "_serial_direct_read"=true'; END;""")
truncate这是与JDBC编写器相关的选项。当SaveMode.Overwrite启用时,该选项的原因星火截断,而不是删除和重建其现有的表。这可以更有效,并防止删除表元数据(例如索引)。但是,在某些情况下(例如,新数据具有不同的架构时),它将不起作用。默认为false。此选项仅适用于写作。
createTableOptions这是与JDBC编写器相关的选项。如果指定,则此选项允许在创建表(例如CREATE TABLE t (name string) ENGINE=InnoDB.)时设置特定于数据库的表和分区选项。此选项仅适用于写作。
createTableColumnTypes创建表时要使用的数据库列数据类型,而不是缺省值。数据类型信息应以与CREATE TABLE列语法相同的格式指定(例如:"name CHAR(64), comments VARCHAR(1024)")。指定的类型应为有效的spark sql数据类型。此选项仅适用于写入。
customSchema用于从JDBC连接器读取数据的自定义架构。例如,"id DECIMAL(38, 0), name STRING"。您还可以指定部分字段,其他部分使用默认类型映射。例如,"id DECIMAL(38, 0)"。列名应与JDBC表的相应列名相同。用户可以指定Spark SQL的相应数据类型,而不必使用默认值。此选项仅适用于阅读。
// Note: JDBC loading and saving can be achieved via either the load/save or jdbc methods
// Loading data from a JDBC source
val jdbcDF = spark.read
  .format("jdbc")
  .option("url", "jdbc:postgresql:dbserver")
  .option("dbtable", "schema.tablename")
  .option("user", "username")
  .option("password", "password")
  .load()

val connectionProperties = new Properties()
connectionProperties.put("user", "username")
connectionProperties.put("password", "password")
val jdbcDF2 = spark.read
  .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)
// Specifying the custom data types of the read schema
connectionProperties.put("customSchema", "id DECIMAL(38, 0), name STRING")
val jdbcDF3 = spark.read
  .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)

// Saving data to a JDBC source
jdbcDF.write
  .format("jdbc")
  .option("url", "jdbc:postgresql:dbserver")
  .option("dbtable", "schema.tablename")
  .option("user", "username")
  .option("password", "password")
  .save()

jdbcDF2.write
  .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)

// Specifying create table column data types on write
jdbcDF.write
  .option("createTableColumnTypes", "name CHAR(64), comments VARCHAR(1024)")
  .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)

2.4.0Troubleshooting(故障排除)

JDBC驱动程序类在客户端会话和所有执行程序上必须对原始类加载器可见。这是因为Java的DriverManager类进行了安全检查,导致它忽略了当打开连接时原始类加载器不可见的所有驱动程序。一种方便的方法是修改所有工作程序节点上的compute_classpath.sh以包括您的驱动程序JAR。
某些数据库(例如H2)会将所有名称都转换为大写。您需要使用大写字母在Spark SQL中引用这些名称。

3.sparksql性能调优

3.1在内存中缓存数据

Spark SQL可以通过调用`spark.catalog.cacheTable("tableName")`或使用内存列式格式缓存表`dataFrame.cache()`。然后,Spark SQL将仅扫描所需的列,并将自动调整压缩以最大程度地减少内存使用和GC压力。您可以调用`spark.catalog.uncacheTable("tableName")`从内存中删除表。
可以使用setConf 上的方法`SparkSession`或`SET key=value`使用SQL 运行 命令来完成内存中缓存的配置。
field默认含义
spark.sql.inMemoryColumnarStorage.compressed真正设置为true时,Spark SQL将根据数据统计信息自动为每一列选择一个压缩编解码器。
spark.sql.inMemoryColumnarStorage.batchSize10000控制用于列式缓存的批处理的大小。较大的批处理大小可以提高内存利用率和压缩率,但是在缓存数据时会出现OOM。

3.2配置选项

以下选项也可以用于调整查询执行的性能。随着自动执行更多优化,这些选项可能会在将来的版本中被弃用。

物业名称默认含义
spark.sql.files.maxPartitionBytes134217728(128 MB)读取文件时打包到单个分区中的最大字节数。
spark.sql.files.openCostInBytes4194304(4 MB)可以同时扫描打开文件的估计成本(以字节数衡量)。将多个文件放入分区时使用。最好高估一下,然后,具有较小文件的分区将比具有较大文件的分区(首先安排)更快。
spark.sql.broadcastTimeout300广播加入中广播等待时间的秒数超时
spark.sql.autoBroadcastJoinThreshold10485760(10 MB)配置表的最大大小(以字节为单位),该表在执行联接时将广播到所有工作程序节点。通过将此值设置为-1,可以禁用广播。请注意,当前仅ANALYZE TABLE COMPUTE STATISTICS noscan运行命令的Hive Metastore表支持统计信息 。
spark.sql.shuffle.partitions200配置在对联接或聚集进行数据混排时要使用的分区数。

3.3SQL查询的广播提示

所述BROADCAST提示导向火花与另一个表或视图接合它们时广播的每个指定的表。当Spark确定连接方法时,即使统计数据高于配置,也首选广播哈希连接(即BHJ)spark.sql.autoBroadcastJoinThreshold。当指定了连接的两端时,Spark广播统计信息较少的一方。注意Spark不能保证始终选择BHJ,因为并非所有情况(例如完全外部联接)都支持BHJ。当选择广播嵌套循环联接时,我们仍然遵守提示。

import org.apache.spark.sql.functions.broadcast
broadcast(spark.table("src")).join(spark.table("records"), "key").show()

3.4分布式SQL引擎

Spark SQL还可以使用其JDBC / ODBC或命令行界面充当分布式查询引擎。在这种模式下,最终用户或应用程序可以直接与Spark SQL交互以运行SQL查询,而无需编写任何代码。

3.5运行Spark SQL CLI

Spark SQL CLI是在本地模式下运行Hive Metastore服务并执行从命令行输入的查询的便捷工具。请注意,Spark SQL CLI无法与Thrift JDBC服务器通信。

要启动Spark SQL CLI,请在Spark目录中运行以下命令:

./bin/spark-sql

hive的结构是通过将您做hive-site.xmlcore-site.xmlhdfs-site.xml文件conf/。您可以运行./bin/spark-sql --help以获取所有可用选项的完整列表。

4.schema操作

schema:
Spark SQL提供的接口为Spark提供了有关数据结构和正在执行的计算的更多信息。在内部,Spark SQL使用这些额外的信息来执行额外的优化
import spark.implicits._
// Print the schema in a tree format
df.printSchema()
// root
// |-- age: long (nullable = true)
// |-- name: string (nullable = true)

// Select only the "name" column

5.不支持的hive

hive的主要功能

带存储桶的表:存储桶是Hive表分区内的哈希分区。Spark SQL还不支持存储桶。
神秘的蜂巢功能

UNION 类型
独特的加入
列统计信息收集:Spark SQL目前不搭载扫描来收集列统计信息,仅支持填充配置单元元存储的sizeInBytes字段。
配置单元输入/输出格式

CLI的文件格式:为了将结果显示回CLI,Spark SQL仅支持TextOutputFormat。
Hadoop档案
hive优化

Spark中尚未包含一些Hive优化。由于Spark SQL的内存中计算模型,其中一些(例如索引)的重要性不那么高。其他版本已投放到Spark SQL的将来版本中。

块级位图索引和虚拟列(用于构建索引)
自动确定联接和groupby的化简器数量:当前在Spark SQL中,您需要使用“ SET spark.sql.shuffle.partitions=[num_tasks];” 控制改组后的并行度。
仅元数据查询:对于仅可使用元数据才能回答的查询,Spark SQL仍会启动任务以计算结果。
偏斜数据标志:Spark SQL不遵循Hive中的偏斜数据标志。
STREAMTABLE连接提示:Spark SQL不遵循STREAMTABLE提示。
合并多个小文件以获取查询结果:如果结果输出包含多个小文件,则Hive可以选择将这些小文件合并为较少的大文件,以避免HDFS元数据溢出。Spark SQL不支持该功能。
蜂巢UDF / UDTF / UDAF

Spark SQL并不支持Hive UDF / UDTF / UDAF的所有API。以下是不受支持的API:

getRequiredJars和getRequiredFiles(UDF和GenericUDF)是自动包含此UDF所需的其他资源的功能。
initialize(StructObjectInspector)GenericUDTF尚不支持in 。Spark SQL当前initialize(ObjectInspector[])仅使用不推荐使用的接口。
configure(GenericUDF,GenericUDTF和GenericUDAFEvaluator)是使用初始化函数的函数MapredContext,不适用于Spark。
close(GenericUDF和GenericUDAFEvaluator)是释放关联资源的功能。任务完成后,Spark SQL不会调用此函数。
reset(GenericUDAFEvaluator)是用于重新初始化聚合以重用相同聚合的函数。Spark SQL当前不支持聚合的重用。
getWindowingEvaluator(GenericUDAFEvaluator)是通过评估固定窗口上的聚合来优化聚合的功能。
不兼容的Hive UDF
以下是Hive和Spark产生不同结果的方案:

SQRT(n) 如果n <0,则Hive返回null,Spark SQL返回NaN。
ACOS(n) 如果n <-1或n> 1,则Hive返回null,Spark SQL返回NaN。
ASIN(n) 如果n <-1或n> 1,则Hive返回null,Spark SQL返回NaN。

果。
偏斜数据标志:Spark SQL不遵循Hive中的偏斜数据标志。
STREAMTABLE连接提示:Spark SQL不遵循STREAMTABLE提示。
合并多个小文件以获取查询结果:如果结果输出包含多个小文件,则Hive可以选择将这些小文件合并为较少的大文件,以避免HDFS元数据溢出。Spark SQL不支持该功能。
蜂巢UDF / UDTF / UDAF

Spark SQL并不支持Hive UDF / UDTF / UDAF的所有API。以下是不受支持的API:

getRequiredJars和getRequiredFiles(UDF和GenericUDF)是自动包含此UDF所需的其他资源的功能。
initialize(StructObjectInspector)GenericUDTF尚不支持in 。Spark SQL当前initialize(ObjectInspector[])仅使用不推荐使用的接口。
configure(GenericUDF,GenericUDTF和GenericUDAFEvaluator)是使用初始化函数的函数MapredContext,不适用于Spark。
close(GenericUDF和GenericUDAFEvaluator)是释放关联资源的功能。任务完成后,Spark SQL不会调用此函数。
reset(GenericUDAFEvaluator)是用于重新初始化聚合以重用相同聚合的函数。Spark SQL当前不支持聚合的重用。
getWindowingEvaluator(GenericUDAFEvaluator)是通过评估固定窗口上的聚合来优化聚合的功能。
不兼容的Hive UDF
以下是Hive和Spark产生不同结果的方案:

SQRT(n) 如果n <0,则Hive返回null,Spark SQL返回NaN。
ACOS(n) 如果n <-1或n> 1,则Hive返回null,Spark SQL返回NaN。
ASIN(n) 如果n <-1或n> 1,则Hive返回null,Spark SQL返回NaN。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值