如何扩展Spark Catalyst,抓取spark sql 语句,通过listenerBus发送sql event以及编写自定义的Spark SQL引擎
1、Spark Catalyst扩展点
Spark catalyst的扩展点在SPARK-18127中被引入,Spark用户可以在SQL处理的各个阶段扩展自定义实现,非常强大高效,是SparkSQL的核心组件(查询优化器),它负责将SQL语句转换成物理执行计划,Catalyst的优劣决定了SQL执行的性能。Catalyst Optimizer是SparkSQL的核心组件(查询优化器),它负责将SQL语句转换成物理执行计划,Catalyst的优劣决定了SQL执行的性能。查询优化器是一个SQL引擎的核心,开源常用的有Apache Calcite(很多开源组件都通过引入Calcite来实现查询优化,如Hive/Phoenix/Drill等),另外一个是orca(HAWQ/GreenPlum中使用)。
2、SparkSessionExtensions
SparkSessionExtensions保存了所有用户自定义的扩展规则,自定义规则保存在成员变量中,对于不同阶段的自定义规则,SparkSessionExtensions提供了不同的接口。
api文档地址:https://spark.apache.org/docs/latest/api/scala/org/apache/spark/sql/SparkSessionExtensions.html
SparkSessionExtensions
classSparkSessionExtensions extends AnyRef
ExperimentalDeveloper API
Holder for injection points to the SparkSession. We make NO guarantee about the stability regarding binary compatibility and source compatibility of methods here. This current provides the following extension points: Analyzer Rules.
Check Analysis Rules.
Optimizer Rules.
Pre CBO Rules.
Planning Strategies.
Customized Parser.
(External) Catalog listeners.
Columnar Rules.
Adaptive Query Stage Preparation Rules.
The extensions can be used by calling withExtensions on the SparkSession.Builder, for example: SparkSession.builder()
.master("...")
.config("...", true)
.withExtensions { extensions =>
extensions.injectResolutionRule { session =>
...
}
extensions.injectParser { (session, parser) =>
...
}
}
.getOrCreate()
The extensions can also be used by setting the Spark SQL configuration property spark.sql.extensions. Multiple extensions can be set using a comma-separated list. For example: SparkSession.builder()
.master("...")
.config("spark.sql.extensions", "org.example.MyExtensions,org.example.YourExtensions")
.getOrCreate() class MyExtensions extends Function1[SparkSessionExtensions, Unit] {
override def apply(extensions: SparkSessionExtensions): Unit = {
extensions.injectResolutionRule { session =>
...
}
extensions.injectParser { (session, parser) =>
...
}
}
} class YourExtensions extends SparkSessionExtensionsProvider {
override def apply(extensions: SparkSessionExtensions): Unit = {
extensions.injectResolutionRule { session =>
...
}
extensions.injectFunction(...)
}
}
Note that none of the injected builders should assume that the SparkSession is fully initialized and should not touch the session's internals (e.g. the SessionState). Annotations
@DeveloperApi() @Experimental() @Unstable()
Source
SparkSessionExtensions.scala
Filter all members
Instance Constructors
newSparkSessionExtensions()
Type Members
typeCheckRuleBuilder = (SparkSession) ⇒ (LogicalPlan) ⇒ Unit
typeColumnarRuleBuilder = (SparkSession) ⇒ ColumnarRule
typeFunctionDescription = (FunctionIdentifier, ExpressionInfo, FunctionBuilder)
typeParserBuilder = (SparkSession, ParserInterface) ⇒ ParserInterface
typeQueryStagePrepRuleBuilder = (SparkSession) ⇒ Rule[SparkPlan]
typeRuleBuilder = (SparkSession) ⇒ Rule[LogicalPlan]
typeStrategyBuilder = (SparkSession) ⇒ Strategy
typeTableFunctionDescription = (FunctionIdentifier, ExpressionInfo, TableFunctionBuilder)
Value Members
definjectCheckRule(builder: CheckRuleBuilder): Unit
Inject an check analysis Rule builder into the SparkSession. definjectColumnar(builder: ColumnarRuleBuilder): Unit
Inject a rule that can override the columnar execution of an executor. definjectFunction(functionDescription: FunctionDescription): Unit
Injects a custom function into the org.apache.spark.sql.catalyst.analysis.FunctionRegistry at runtime for all sessions. definjectOptimizerRule(builder: RuleBuilder): Unit
Inject an optimizer Rule builder into the SparkSession. definjectParser(builder: ParserBuilder): Unit
Inject a custom parser into the SparkSession. definjectPlannerStrategy(builder: StrategyBuilder): Unit
Inject a planner Strategy builder into the SparkSession. definjectPostHocResolutionRule(builder: RuleBuilder): Unit
Inject an analyzer Rule builder into the SparkSession. definjectPreCBORule(builder: RuleBuilder): Unit
Inject an optimizer Rule builder that rewrites logical plans into the SparkSession. definjectQueryStagePrepRule(builder: QueryStagePrepRuleBuilder): Unit
Inject a rule that can override the query stage preparation phase of adaptive query execution. definjectResolutionRule(builder: RuleBuilder): Unit
Inject an analyzer resolution Rule builder into the SparkSession. definjectTableFunction(functionDescription: TableFunctionDescription): Unit
Injects a custom function into the org.apache.spark.sql.catalyst.analysis.TableFunctionRegistry at runtime for all sessions.
转载请注明张永清 博客园:https://www.cnblogs.com/laoqing/p/16351482.html
2.1 新增自定义规则
用户可以通过SparkSessionExtensions提供的inject开头的方法添加新的自定义规则,具体的inject接口如下:
- injectOptimizerRule – 添加optimizer自定义规则,optimizer负责逻辑执行计划的优化。
- injectParser – 添加parser自定义规则,parser负责SQL解析。
- injectPlannerStrategy – 添加planner strategy自定义规则,planner负责物理执行计划的生成。
- injectResolutionRule – 添加Analyzer自定义规则到Resolution阶段,analyzer负责逻辑执行计划生成。
- injectPostHocResolutionRule – 添加Analyzer自定义规则到Post Resolution阶段。
- injectCheckRule – 添加Analyzer自定义Check规则。
Spark Catalyst的SQL处理分成parser,analyzer,optimizer以及planner等多个步骤,其中analyzer,optimizer等步骤内部也分为多个阶段,以Analyzer为例,analyse规则切分到不同的batch中,每个batch的执行策略可能不尽相同,有的只会执行一遍,有的会迭代执行直到满足一定条件。
2.2 获取自定义规则
SparkSessionExtensions对应每一种自定义规则也都有一个build开头的方法用于获取对应类型的自定义规则,Spark session在初始化的时候,通过这些方法获取自定义规则并传递给parser,analyzer,optimizer以及planner等对象。
- buildOptimizerRules
- buildParser
- buildPlannerStrategies
- buildResolutionRules
- buildPostHocResolutionRules
- buildCheckRules
2.3 配置自定义规则
在Spark中,用户自定义的规则可以通过两种方式配置生效:
- 使用SparkSession.Builder中的withExtenstion方法,withExtension方法是一个高阶函数,接收一个自定义函数作为参数,这个自定义函数以SparkSessionExtensions作为参数,用户可以实现这个函数,通过SparkSessionExtensions的inject开头的方法添加用户自定义规则。
- 通过Spark配置参数,具体参数名为spark.sql.extensions。用户可以将1中的自定义函数实现定义为一个类,将完整类名作为参数值。
具体的用法用户可以参考org.apache.spark.sql.SparkSessionExtensionSuite测试用例中的Spark代码。
3、扩展Spark Catalyst
3.1 通过listenerBus发送sql event
package org.apache.spark.sql.execution
//转载请注明张永清 博客园:https://www.cnblogs.com/laoqing/p/16351482.html
import org.apache.spark.internal.Logging
import org.apache.spark.sql.SparkSession case class SqlEvent(sqlText: String, sparkSession: SparkSession) extends org.apache.spark.scheduler.SparkListenerEvent with Logging class MySqlParser(sparkSession: SparkSession, val delegate : org.apache.spark.sql.catalyst.parser.ParserInterface) extends scala.AnyRef with org.apache.spark.sql.catalyst.parser.ParserInterface with Logging{
override def parsePlan(sqlText: String): LogicalPlan = {
logInfo("start to send SqlEvent by listenerBus,sqlText:"+sqlText)
sparkSession.sparkContext.listenerBus.post( SqlEvent(sqlText,sparkSession))
logInfo("send SqlEvent success by listenerBus,sqlText:"+sqlText)
delegate.parsePlan(sqlText)
} override def parseExpression(sqlText: String): Expression = {
delegate.parseExpression(sqlText)
} override def parseTableIdentifier(sqlText: String): TableIdentifier = {
delegate.parseTableIdentifier(sqlText) } override def parseFunctionIdentifier(sqlText: String): FunctionIdentifier = {
delegate.parseFunctionIdentifier(sqlText) } override def parseTableSchema(sqlText: String): StructType = {
delegate.parseTableSchema(sqlText) } override def parseDataType(sqlText: String): DataType = {
delegate.parseDataType(sqlText)
} }
import org.apache.spark.sql.SparkSessionExtensions
class MySparkSessionExtension extends ((SparkSessionExtensions) => Unit) {
override def apply(extensions: SparkSessionExtensions): Unit = {
extensions.injectParser { (session, parser) =>
new MySqlParser(session, parser)
}
}
}
SparkSession.builder()
.master("...")
.config("spark.sql.extensions", "MySparkSessionExtension") .getOrCreate()
还有一种抓取sql 语句的方法,这里顺带介绍一下:
通过aspectj 对 spark 进行拦截 抓取sql,引入如下pom
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.1</version>
</dependency> <dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
并且引入如下maven plugin
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.11</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
<showWeaveInfo>true</showWeaveInfo>
<Xlint>ignore</Xlint>
<encoding>UTF-8</encoding>
<skip>true</skip>
<forceAjcCompile>true</forceAjcCompile>
<weaveDirectories>
<weaveDirectory>${project.build.outputDirectory}</weaveDirectory>
</weaveDirectories>
</configuration> <executions> <execution> <configuration> <skip>false</skip> </configuration> <goals> <goal>compile</goal> </goals> </execution> </executions> </plugin>
拦截示例:
import org.apache.spark.sql.{Dataset, Row, SparkSession}
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.{Around, Aspect}
@Aspect
class SparkSqlAspect {
@Around("execution(public org.apache.spark.sql.Dataset<org.apache.spark.sql.Row> org.apache.spark.sql.SparkSession.sql(java.lang.String)) && args(sqlRaw)")
def around(pjp: ProceedingJoinPoint, sqlRaw: String): Dataset[Row] = {
val sql = sqlRaw.trim
val spark = pjp.getThis.asInstanceOf[SparkSession]
val userId = spark.sparkContext.sparkUser
val tables = getTables(sql, spark)
if (accessControl(userId, tables)) {
pjp.proceed(pjp.getArgs).asInstanceOf[Dataset[Row]]
} else {
throw new IllegalAccessException("access failed")
}
}
def getTables(query: String,
spark: SparkSession): Seq[String] = {
val logicalPlan = spark.sessionState.sqlParser.parsePlan(query)
import org.apache.spark.sql.catalyst.analysis.UnresolvedRelation
logicalPlan.collect { case r: UnresolvedRelation => r.tableName }
}
def accessControl(user: String,
table: Seq[String]): Boolean = {
println("userId: " + user, "\n tableName: " + table.mkString(","))
true
}
}
测试示例:
import org.apache.spark.sql.SparkSession
object SparkAspectJTest {
def main(args: Array[String]): Unit = {
val spark = SparkSession.
builder().
master("local[1]").
appName("test").
enableHiveSupport().
getOrCreate()
spark.range(5).createTempView("t1")
spark.range(10).createTempView("t2")
spark.sql("select * from t1 left join t2 on t1.id = t2.id").explain()
}
}
3.2创建一个自定义Parser
class StrictParser(parser: ParserInterface) extends ParserInterface {
/**
* Parse a string to a [[LogicalPlan]].
*/
override def parsePlan(sqlText: String): LogicalPlan = {
val logicalPlan = parser.parsePlan(sqlText)
logicalPlan transform {
case project @ Project(projectList, _) =>
projectList.foreach {
name =>
if (name.isInstanceOf[UnresolvedStar]) {
throw new RuntimeException("You must specify your project column set," +
" * is not allowed.")
}
}
project
}
logicalPlan
}
/**
* Parse a string to an [[Expression]].
*/
override def parseExpression(sqlText: String): Expression = parser.parseExpression(sqlText)
/**
* Parse a string to a [[TableIdentifier]].
*/
override def parseTableIdentifier(sqlText: String): TableIdentifier =
parser.parseTableIdentifier(sqlText)
/**
* Parse a string to a [[FunctionIdentifier]].
*/
override def parseFunctionIdentifier(sqlText: String): FunctionIdentifier =
parser.parseFunctionIdentifier(sqlText)
/**
* Parse a string to a [[StructType]]. The passed SQL string should be a comma separated
* list of field definitions which will preserve the correct Hive metadata.
*/
override def parseTableSchema(sqlText: String): StructType =
parser.parseTableSchema(sqlText)
/**
* Parse a string to a [[DataType]].
*/
override def parseDataType(sqlText: String): DataType = parser.parseDataType(sqlText)
}
创建扩展点函数
type ParserBuilder = (SparkSession, ParserInterface) => ParserInterface
type ExtensionsBuilder = SparkSessionExtensions => Unit
val parserBuilder: ParserBuilder = (_, parser) => new StrictParser(parser)
val extBuilder: ExtensionsBuilder = { e => e.injectParser(parserBuilder)}
extBuilder函数用于SparkSession构建,SparkSessionExtensions.injectParser函数本身也是一个高阶函数,接收parserBuilder作为参数,将原生parser作为参数传递给自定义的StrictParser,并将StrictParser作为自定义parser插入SparkSessionExtensions中。
在SparkSession中启用自定义Parser
val spark = SparkSession
.builder()
.appName("Spark SQL basic example")
.config("spark.master", "local[2]")
.withExtensions(extBuilder)
.getOrCreate()
在Spark2.2版本中,引入了新的扩展点,使得用户可以在Spark session中自定义自己的parser,analyzer,optimizer以及physical planning stragegy rule。通过两个简单的示例,我们展示了如何通过Spark提供的扩展点实现SQL检查以及定制化的执行计划优化。Spark Catalyst高度的可扩展性使得我们可以非常方便的定制适合自己实际使用场景的SQL引擎,拓展了更多的可能性。我们可以实现特定的SQL方言,针对特殊的数据源做更深入的优化,进行SQL规范检查,针对特定执行环境制定特定的优化策略等等。
如何扩展Spark Catalyst,抓取spark sql 语句,通过listenerBus发送sql event以及编写自定义的Spark SQL引擎的更多相关文章
- sqlserver 抓取所有执行语句 SQL语句分析 死锁 抓取
原文:sqlserver 抓取所有执行语句 SQL语句分析 死锁 抓取 在多人开发中最头疼的是人少事多没有时间进行codereview,本来功能都没时间写,哪有时间来开会细细来分析代码.软件能跑就行, ...
- scrapy抓取拉勾网职位信息(三)——爬虫rules内容编写
在上篇中,分析了拉勾网需要跟进的页面url,本篇开始进行代码编写. 在编写代码前,需要对scrapy的数据流走向有一个大致的认识,如果不是很清楚的话建议先看下:scrapy数据流 本篇目标:让拉勾网爬 ...
- 抓取awr、语句级awr、ashrpt
exec dbms_workload_repository.create_snapshot();--调用MMON进程立即收集快照 生成AWR报告@?/rdbms/admin/awrrpt.sql; 9 ...
- Sql语句构造类,多字段新增或修改时,拼装sql语句比较方便
using System; using System.Collections.Generic; using System.Text; namespace MSCL { #region 使用示例 /* ...
- EF5中 执行 sql语句使用Database.ExecuteSqlCommand 返回影响的行数 ; EF5执行sql查询语句 Database.SqlQuery 带返回值
一: 执行sql语句,返回受影响的行数 在mysql里面,如果没有影响,那么返回行数为 -1 ,sqlserver 里面 还没有测试过 using (var ctx = new MyDbConte ...
- SQL Server 2008 R2,显示SQL语句执行窗口。 编辑前200行,可以执行SQL语句
- 记一次企业级爬虫系统升级改造(二):基于AngleSharp实现的抓取服务
爬虫系统升级改造正式启动: 在第一篇文章,博主主要介绍了本次改造的爬虫系统的业务背景与全局规划构思: 未来Support云系统,不仅仅是爬虫系统,是集爬取数据.数据建模处理统计分析.支持全文检索资源库 ...
- 【Java EE 学习 48】【Hibernate学习第五天】【抓取策略】【二级缓存】【HQL】
一.抓取策略. 1.hibernate中提供了三种抓取策略. (1)连接抓取(Join Fetch):这种抓取方式是默认的抓取方式.使用这种抓取方式hibernate会在select中内连接的方式获取 ...
- Hibernate 抓取策略fetch-1 (select join subselect)
原文 :http://4045060.blog.51cto.com/4035060/1088025 部分参考:http://www.cnblogs.com/rongxh7/archive/2010/0 ...
- Hibernate 性能优化之抓取策略
fetch 抓取策略 前提条件:必须是一个对象操作其关联对象. 1. 根据一的一方加载多的一方,在一的一方集合中,有三个值:join/select/subselect 2.根据多的一方加载一的一方, ...
随机推荐
- windwos10-11打开任意文件弹出警告
如下打开exe或者视频.图片都弹出警告 解决方案输入快捷键win+s换出搜索框 输入Internet 选项 进入安全选项点击自定义级别 找到,加载应用程序和不安全文件 勾选启用(不安全) 然后确定-在 ...
- python:spacy、gensim库的安装遇到问题及bug处理
1.spacy SpaCy最新版V3.0.6版,在CMD 模式下可以通过 pip install spacy -U 进行安装 注意这个过程进行前可以先卸载之前的旧版本 pip uninstall sp ...
- 从嘉手札<2023-11-18>
随便补一个~ 1.我也不是不快乐,我其实挺快乐的,和朋友出去玩,看电影,刷搞笑视频,我都能表现的很好,但这些都不是真正让我感受到快乐的东西,它就像膝跳反应一样,碰我一下我就会笑,但笑完就结束了.甚至在 ...
- 【奶奶看了也不会】微信群聊(微信客服)接入ChatGPT教程
1.聊天效果展示 大家好,我是小卷.最近工作变卷了,都已经一个月没更新文章了.今天来教教大家怎么给微信群聊的智能客服接入ChatGPT.和之前企业微信机器人不同的是,这次是可以外部微信群使用的.用的人 ...
- 一个关于用netty的小错误反思
一个关于用netty的小认知 在使用netty时,观看了黑马的netty网课,没想就直接用他的依赖了 依赖如下 <dependency> <groupId>io.netty&l ...
- [Java]format string is malformed java
format string is malformed java 最近在做代码审查,发现很多在使用 String.format 的时候遇到了IDEA报的 Format string 'xxx' is m ...
- C# 二十年语法变迁之 C# 8参考
C# 二十年语法变迁之 C# 8参考 自从 C# 于 2000 年推出以来,该语言的规模已经大大增加,我不确定任何人是否有可能在任何时候都对每一种语言特性都有深入的了解.因此,我想写一系列快速参考文章 ...
- JS leetcode 多数元素 题解分析
壹 ❀ 引 做题做题,再忙每天都要抽空做一道题!今天来做一道有趣的题,题目来自多数元素,题目描述如下: 给定一个大小为 n 的数组,找到其中的多数元素.多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ ...
- Event对象
Event对象 Event对象表示在DOM中出现的事件,在DOM中有许多不同类型的事件,其主要使用基于Event对象作为主接口的二次接口,Event对象本身包含适用于所有事件的属性和方法. 描述 事件 ...
- Java实现文件下载断点续传(一)
参考文章:https://www.ibm.com/developerworks/cn/java/joy-down/ 1.原理介绍 想象一下我们下载一个10G的文件,当下载到9.99G的时候断网了... ...