地表最强系列之Spark SQL

Spark SQL架构

  • Spark SQL是Spark的核心组件之一(2014.4 Spark1.0)
  • 能够直接访问现存的Hive数据
  • 提供JDBC/ODBC接口供第三方工具借助Spark进行数据处理
  • 提供了更高层级的接口方便地处理数据
  • 支持多种操作方式:SQL、API编程
  • 支持多种外部数据源:Parquet、JSON、RDBMS等

Spark SQL运行原理

Catalyst优化器是Spark SQL的核心,所有 SQL 操作最终都通过 Catalyst 翻译成类
似的 Spark 程序代码被 Spark Core 调度执行,其过程也有 Job、Stage、Task 的概
念。如下图所示。
在这里插入图片描述

  • SQL 执行过程

对于 Spark SQL 来说,从 SQL 到 RDD 的执行需要经过两个大的阶段,分别
是逻辑计划和物理计划。Catalyst 优化器,作用便是将逻辑计划转为物理计划。
如下图所示。
SQL 执行过程-1 SQL 执行过程-1
在这里插入图片描述
SQL 执行过程-2

Catalyst优化器

1.逻辑计划

逻辑计划阶段会将用户所写的 SQL 语句转换成树型数据结构(逻辑算子
树),SQL 语句中蕴含的逻辑映射到逻辑算子树的不同节点。顾名思义,逻辑计
划阶段生成的逻辑算子树并不会直接提交执行,仅作为中间阶段。最终逻辑算子
树的生成过程经历三个子阶段,分别对应:
1)未解析的逻辑算子树(Unresolved LogicaPlan ,仅仅是数据结构,不包
含任何数据信息等);
2)解析后的逻辑算子树(Analyzed LogicalPlan ,节点中绑定各种信息);
3)优化后的逻辑算子树(Optimized LogcalPlan ,应用各种优化规则对一些
低效的逻辑计划进行转换)。
物理计划阶段将上一步逻辑计划阶段生成的逻辑算子树进行进一步转换,生
成物理算子树。物理算子树的节点会直接生成 RDD 或对 RDD 进行

transformation 操作(注:每个物理计划节点中都实现了对 RDD 进行转换的
execute 方法)。同样地,物理计划阶段也包含三个子阶段:
1)首先,根据逻辑算子树,生成物理算子树的列表 Iterator[PhysicaPlan] (同
样的逻辑算子树可能对应多个物理算子树);
2)然后,从列表中按照一定的策略选取最优的物理算子树(SparkPlan );
3)最后,对选取的物理算子树进行提交前的准备工作,例如,确保分区操

作正确、物理算子树节点重用、执行代码生成等,得到“准备后”的物理算子树
(Prepared SparkPlan)。
经过上述步骤后,物理算子树生成的 RDD 执行 action 操作,即可提交执
行。
从 SQL 语句的解析一直到提交之前,上述整个转换过 Spark 集群 Driver
端进行,不涉及分布式环境。SparkSession 类的 sql 方法调用 SessionState 中的
各种对象,包括上述不同阶段对应的 SparkSqlParser 类、 Analyzer 类、
Optimizer SparkPlanner 类等,最后封装成 Query Execution 对象。因此,在进行
Spark SQL 开发时,可以很方便地将每一步生成的计划单独剥离出来分析。
如下图所示,左上角是 SQL 语句,生成的逻辑算子树中有 Relation、Filter、
Project 节点,分别对应数据 、过滤逻辑( age>18 )和列剪裁逻辑 。下一步
的物理算子树从逻辑算子树一对一映射得到,Relation 逻辑节点转换为
FileSourceScanExec 执行节点,Filter 逻辑节点转换为 FilterExec 执行节点,
Project 逻辑节点转换为 ProjectExec 执行节点。

2.优化
在投影上面查询过滤器
检查过滤是否可下压
在这里插入图片描述
3.物理计划
在这里插入图片描述

Dataset

特定域对象中的强类型集合
1、createDataset()的参数可以是:Seq、Array、RDD
2、上面三行代码生成的Dataset分别是:
Dataset[Int]、Dataset[(String,Int)]、Dataset[(String,Int,Int)]
3、Dataset=RDD+Schema,所以Dataset与RDD有大部共同的函数,如map、filter等

scala> spark.createDataset(1 to 3).show
scala> spark.createDataset(List(("a",1),("b",2),("c",3))).show
scala> spark.createDataset(sc.parallelize(List(("a",1,1),("b",2,2)))).show

使用Case Class创建Dataset

object Demo3 {
  //创建样例类
  case class Point1(label:String,x:Double,y:Double)
  case class Category1(id:Long,name:String)

  def main(args: Array[String]): Unit = {
    // 创建一个SparkSession对象
    val spark = SparkSession.builder().master("local[*]").appName("aa").getOrCreate()
    val sc = spark.sparkContext
    //DS的包
    import spark.implicits._
    //todo 创建一个RDD
    val categoriesRDD=sc.parallelize(List((1,"foo"),(2,"bar")))
    //ToDO 使用Spark对象来创建Dataset
    val value: Dataset[sparksql.Demo3.Category1] = categoriesRDD.map(x=>Category1(x._1, x._2)).toDS()
    value.show()
  }
}

DataFrame

1.DataFrame=Dataset[Row]
2.类似传统数据的二维表格
3.在RDD基础上加入了Schema(数据结构信息)
4.DataFrame Schema支持嵌套数据类型:struct,map.array
5.提供更多类似SQL操作的API
创建DataFrame

 /** 将JSON文件转成DataFrame
      * people.json内容如下
      * {"name":"Michael"}
      * {"name":"Andy", "age":30}
      * {"name":"Justin", "age":19}
      */
val df = spark.read.json("file:///home/hadoop/data/people.json")
// 使用show方法将DataFrame的内容输出
df.show
  • DataFrame API常用操作
val df = spark.read.json("file:///home/hadoop/data/people.json")
// 使用printSchema方法输出DataFrame的Schema信息
df.printSchema()
// 使用select方法来选择我们所需要的字段
df.select("name").show()
// 使用select方法选择我们所需要的字段,并未age字段加1
df.select(df("name"), df("age") + 1).show()
//  使用filter方法完成条件过滤
df.filter(df("age") > 21).show()
// 使用groupBy方法进行分组,求分组后的总数
df.groupBy("age").count().show()
//sql()方法执行SQL查询操作
df.createOrReplaceTempView("people")
spark.sql("SELECT * FROM people").show
  • RDD->DF
object Demo6 extends App {
  val spark = SparkSession.builder().master("local[*]").appName("aa").getOrCreate()
  val sc = spark.sparkContext
  import spark.implicits._
  private val textRDD: RDD[Array[String]] = sc.textFile("D:\\IDEA\\_20200806_sparktest\\data\\people.txt").map(_.split(","))
  //todo 自定义一个schema信息
  //zhangsan,29
  private val schema = StructType(Array(
    StructField("name", StringType, true),
    StructField("age", IntegerType, true)
  ))

  //todo 把rdd转换成Row
  private val makeRDD: RDD[Row] = textRDD.map(x=>Row(x(0),x(1).trim.toInt))
  //todo 把RDD转换成DataFrame
  private val df1: DataFrame = spark.createDataFrame(makeRDD,schema)
  df1.printSchema()
  df1.show()
  private val rddres: RDD[Row] = df1.rdd
  println(rddres.collect().mkString(" "))


  • DataFrame ->RDD
 /** people.json内容如下
      * {"name":"Michael"}
      * {"name":"Andy", "age":30}
      * {"name":"Justin", "age":19}
      */
val df = spark.read.json("file:///home/hadoop/data/people.json")
//将DF转为RDD
df.rdd.collect
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在信号处理领域,DOA(Direction of Arrival)估计是一项关键技术,主要用于确定多个信号源到达接收阵列的方向。本文将详细探讨三种ESPRIT(Estimation of Signal Parameters via Rotational Invariance Techniques)算法在DOA估计中的实现,以及它们在MATLAB环境中的具体应用。 ESPRIT算法是由Paul Kailath等人于1986年提出的,其核心思想是利用阵列数据的旋转不变性来估计信号源的角度。这种算法相比传统的 MUSIC(Multiple Signal Classification)算法具有较低的计算复杂度,且无需进行特征值分解,因此在实际应用中颇具优势。 1. 普通ESPRIT算法 普通ESPRIT算法分为两个主要步骤:构造等效旋转不变系统和估计角度。通过空间平移(如延时)构建两个子阵列,使得它们之间的关系具有旋转不变性。然后,通过对子阵列数据进行最小二乘拟合,可以得到信号源的角频率估计,进一步转换为DOA估计。 2. 常规ESPRIT算法实现 在描述中提到的`common_esprit_method1.m`和`common_esprit_method2.m`是两种不同的普通ESPRIT算法实现。它们可能在实现细节上略有差异,比如选择子阵列的方式、参数估计的策略等。MATLAB代码通常会包含预处理步骤(如数据归一化)、子阵列构造、旋转不变性矩阵的建立、最小二乘估计等部分。通过运行这两个文件,可以比较它们在估计精度和计算效率上的异同。 3. TLS_ESPRIT算法 TLS(Total Least Squares)ESPRIT是对普通ESPRIT的优化,它考虑了数据噪声的影响,提高了估计的稳健性。在TLS_ESPRIT算法中,不假设数据噪声是高斯白噪声,而是采用总最小二乘准则来拟合数据。这使得算法在噪声环境下表现更优。`TLS_esprit.m`文件应该包含了TLS_ESPRIT算法的完整实现,包括TLS估计的步骤和旋转不变性矩阵的改进处理。 在实际应用中,选择合适的ESPRIT变体取决于系统条件,例如噪声水平、信号质量以及计算资源。通过MATLAB实现,研究者和工程师可以方便地比较不同算法的效果,并根据需要进行调整和优化。同时,这些代码也为教学和学习DOA估计提供了一个直观的平台,有助于深入理解ESPRIT算法的工作原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值