Spark SQL 之自定义删除外部表
前言
Spark SQL 在删除外部表时,本不能删除外部表的数据的。本篇文章主要介绍如何修改Spark SQL 源码实现在删除外部表的时候,可以带额外选项来删除外部表的数据。
本文的环境是我一直使用的 spark 2.4.3 版本。
1. 修改ANTLR4 语法文件
修改 SqlBase.g4文件中drop Table 相关语句,添加(WITH DATA)?, 修改完之后如下:
DROP TABLE (IF EXISTS)? tableIdentifier (WITH DATA)? PURGE? #dropTable
因为,删除external表也不是必须的,所以添加WITH DATA 为可选项,跟 IF EXISTS类似。
2. 修改相关方法
2.1 修改SparkSqlParser.scala文件
/**
* Create a [[DropTableCommand]] command.
*/
override def visitDropTable(ctx: DropTableContext): LogicalPlan = withOrigin(ctx) {
DropTableCommand(
visitTableIdentifier(ctx.tableIdentifier),
ctx.EXISTS != null,
ctx.VIEW != null,
ctx.PURGE != null,
ctx.WITH() != null && ctx.DATA() != null)
}
2.2 修改DropTableCommand.scala等相关文件
首先修改构造函数,在最后一个参数后面添加withData方法,默认为false:
case class DropTableCommand(
tableName: TableIdentifier,
ifExists: Boolean,
isView: Boolean,
purge: Boolean,
withData:Boolean = false // TODO 外部表是否需要删除表数据
) extends RunnableCommand
DropTableCommand本质上其实是用了command设计模式,实际在运行时,会调用其run方法,修改 run 方法,如下:
override def run(sparkSession: SparkSession): Seq[Row] = {
val catalog = sparkSession.sessionState.catalog
val isTempView = catalog.isTemporaryTable(tableName)
if (!isTempView && catalog.tableExists(tableName)) {
// If the command DROP VIEW is to drop a table or DROP TABLE is to drop a view
// issue an exception.
catalog.getTableMetadata(tableName).tableType match {
case CatalogTableType.VIEW if !isView =>
throw new AnalysisException(
"Cannot drop a view with DROP TABLE. Please use DROP VIEW instead")
case o if o != CatalogTableType.VIEW && isView =>
throw new AnalysisException(
s"Cannot drop a table with DROP VIEW. Please use DROP TABLE instead")
case _ =>
}
}
if (isTempView || catalog.tableExists(tableName)) {
try {
sparkSession.sharedState.cacheManager.uncacheQuery(
sparkSession.table(tableName), cascade = !isTempView)
} catch {
case NonFatal(e) => log.warn(e.toString, e)
}
catalog.refreshTable(tableName)
log.warn(s"withData:${withData}")
catalog.dropTable(tableName, ifExists, purge, withData)
} else if (ifExists) {
// no-op
} else {
throw new AnalysisException(s"Table or view not found: ${tableName.identifier}")
}
Seq.empty[Row]
}
在第 28 行,为 catalog对象的dropTable 添加 withData 参数。其中catalog是 org.apache.spark.sql.catalyst.catalog.SessionCatalog 的实例。其子类并没有重写其 dropTable 方法,故只需要修改其dropTable 方法即可。具体修改代码如下:
/**
* Drop a table.
*
* If a database is specified in `name`, this will drop the table from that database.
* If no database is specified, this will first attempt to drop a temporary view with
* the same name, then, if that does not exist, drop the table from the current database.
*/
def dropTable(
name: TableIdentifier,
ignoreIfNotExists: Boolean,
purge: Boolean,
withData:Boolean = false // 外部表是否需要在hdfs上删除其对应的数据
): Unit = synchronized {
val db = formatDatabaseName(name.database.getOrElse(currentDb))
val table = formatTableName(name.table)
if (db == globalTempViewManager.database) {
val viewExists = globalTempViewManager.remove(table)
if (!viewExists && !ignoreIfNotExists) {
throw new NoSuchTableException(globalTempViewManager.database, table)
}
} else {
if (name.database.isDefined || !tempViews.contains(table)) {
requireDbExists(db)
// When ignoreIfNotExists is false, no exception is issued when the table does not exist.
// Instead, log it as an error message.
if (tableExists(TableIdentifier(table, Option(db)))) {
logError(s"withData :${withData}")
externalCatalog.dropTable(db, table, ignoreIfNotExists = true,purge = purge, withData)
} else if (!ignoreIfNotExists) {
throw new NoSuchTableException(db = db, table = table)
}
} else {
tempViews.remove(table)
}
}
}
为防止在test中有很多的测试类在调用该方法,在编译时报错,新添加的withData给默认值,为false,保证该方法默认行为跟之前未修改前一致。
withData 参数继续传递给 externalCatalog.dropTable 方法,其中,externalCatalog 是 org.apache.spark.sql.catalyst.catalog.ExternalCatalog 类型变量,ExternalCatalog 是一个trait,ExternalCatalog 实现类关系如下:

首先修改ExternalCatalog 的dropTable 方法,如下:
def dropTable(
db: String,
table: String,
ignoreIfNotExists: Boolean,
purge: Boolean,
withData:Boolean=false): Unit
参数加载最后,给默认值false。
org.apache.spark.sql.catalyst.catalog.ExternalCatalogWithListener 是一个包装类,其内部在原来ExternalCatalog 的行为之外添加了监听的行为。先修改这个包装类的dropTable,如下:
override def dropTable(
db: String,
table: String,
ignoreIfNotExists: Boolean,
purge: Boolean,
withData:Boolean): Unit = {
postToAll(DropTablePreEvent(db, table))
delegate.dropTable(db, table, ignoreIfNotExists, purge, withData)
postToAll(DropTableEvent(db, table))
}
其中,delegate 就是真正执行 dropTable操作的ExternalCatalog对象。
catlog有两个来源,分别是 in-memory和 hive, in-memory的实现类是org.apache.spark.sql.catalyst.catalog.InMemoryCatalog,只需要添加 方法参数列表即可,在方法内部不需要做任何操作。
hive的实现类是 org.apache.spark.sql.hive.HiveExternalCatalog, 其dropTable 方法如下:
override def dropTable(
db: String,
table: String,
ignoreIfNotExists: Boolean,
purge: Boolean,
withData:Boolean): Unit = withClient {
requireDbExists(db)
val tableLocation: URI = client.getTable(db,table).location
client.dropTable(db, table, ignoreIfNotExists, purge)
val path: Path = new Path(tableLocation)
val fileSystem: FileSystem = FileSystem.get(hadoopConf)
val fileExists: Boolean = fileSystem.exists(path)
logWarning(s"withData:${withData}, ${path} exists : ${fileExists}")
if (withData && fileExists) {
fileSystem.delete(path, true)
}
}
3. 打包编译
在生产环境编译,编译命令如下:
./dev/-cdh5./bin/mvn -Pyarn -Phadoop--cdh5.14.0 -X
注:由于编译的是 cdh版本,一些jar包不在中央仓库,在pom.xml文件中,添加 cloudera maven 源:
<repository> <id>cloudera</id> <url>https://repository.cloudera.com/artifactory/cloudera-repos</url> </repository>
为了加快 maven编译的速度, 在 make-distribution.sh 文件中,修改了编译的并行度,在171行,把1C改为4C,具体修改如下:
BUILD_COMMAND=("$MVN" -T 4C clean package -DskipTests $@)
执行编译结束之后,在项目的根目录下,会有 spark-2.4.3-bin-2.6.0-cdh5.14.0.tgz 这个压缩包,这就是binary 文件,可以解压到指定目录进行相应配置了。
4. 配置spark
把原来集群中spark 的配置以及相关jar包拷贝到新的spark相应目录。
5. 测试
5.1 创建外部表
spark sql
spark-sql> use test;
spark-sql> create external table ext1 location '/user/hive/warehouse/test.db/ext1' as select * from person;
spark-sql> select * from ext1;
1 2 3
2 zhangsan 4
3 lisi 5
4 wangwu 6
5 rose 7
6 nose 8
7 info 9
8 test 10
查看 hdfs 上对应目录是否有数据
[root@xxx ~]# hdfs dfs -ls -R /user/hive/warehouse/test.db/ext1 -rwxr-xr-x root supergroup -- : /user/hive/warehouse/test.db/ext1/part--aae237ac-4a0b-425c-a0f1-5d54d1e88957-c000
5.2 删除表
spark-sql> drop table if exists ext1 with data;
5.3 验证表元数据已删除成功
spark-sql> show tables; test person false
没有ext表,说明已删除成功。
5.4 验证hdfs上数据已删除成功
[root@node01 ~]# hdfs dfs -ls -R /user/hive/warehouse/test.db/ext1 ls: `/user/hive/warehouse/test.db/ext1': No such file or directory
该目录已不存在,说明hdfs上数据已删除成功。
总结
本文具体介绍了如何修改spark sql 的源码,在删除external表时可选择地删除hdfs上的底层数据。
Spark SQL 之自定义删除外部表的更多相关文章
- 【转载】Spark SQL之External DataSource外部数据源
http://blog.csdn.net/oopsoom/article/details/42061077 一.Spark SQL External DataSource简介 随着Spark1.2的发 ...
- Spark SQL之External DataSource外部数据源(二)源代码分析
上周Spark1.2刚公布,周末在家没事,把这个特性给了解一下,顺便分析下源代码,看一看这个特性是怎样设计及实现的. /** Spark SQL源代码分析系列文章*/ (Ps: External Da ...
- [SQL]修改和删除基本表
修改基本表 SQL语言用alter table语句修改基本表,其一般格式如下: alter table <表名> add <列名> <数据类型> [<列级完整 ...
- sql*loader以及oracle外部表加载Date类型列
Oracle sqlldr LOAD DATAINFILE *INTO TABLE testFIELDS TERMINATED BY X'9'TRAILING NULLCOLS( c2 &quo ...
- persistent.xml hibernate 利用sql script 自定义生成 table 表
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http:// ...
- Sql Server批量删除指定表
--批量删除以test的表开头的表 declare @name varchar(50) while(exists(select * from sysobjects where name like te ...
- sql server 批量删除数据表
SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGO-- =============================================-- Auth ...
- SQL 更新修改删除一个表,库存自动增减的写法
create trigger tri_asbon asb for insert as begin declare @rk int declare @ck int declare @sid varcha ...
- postgres 删除外部表
drop external table if exists tableName;
随机推荐
- NameError:name ‘xrange’ is not defined
原因: 在Python 3中,range()与xrange()合并为range( ). 我的python版本为python3.5. 解决办法: 将xrange( )函数全部换为range( ).
- Java架构师线上问题排查,这些命令程序员一定用得到!
Java架构师线上问题排查,这些命令程序员一定用得到! 线上问题排查,以下场景,你遇到过吗? 一.了解机器连接数情况 问题:1.2.3.4的sshd的监听端口是22,如何统计1.2.3.4的sshd服 ...
- Codeforces Round #615 (Div. 3) 题解
A - Collecting Coins 题意: 给你四个数a,b,c,d,n.问你是否能将n拆成三个数A,B,C,使得A+a=B+b=C+c. 思路: 先计算三个数的差值的绝对值abs,如果abs大 ...
- 软件工程概论 网站开发要掌握的技术 &登录界面
1.网站系统开发需要掌握的技术 一.界面和用户体验(Interface and User Experience) 1.1 知道如何在基本不影响用户使用的情况下升级网站.通常来说,你必须有版本控制系统( ...
- 每天玩转3分钟 MyBatis-Plus - 6. select 用法
每天玩转3分钟 MyBatis-Plus - 1. 配置环境 每天玩转3分钟 MyBatis-Plus - 2. 普通查询 每天玩转3分钟 MyBatis-Plus - 3. 高级查询(一) 每天玩转 ...
- jQuery学习总结(三)
这篇文章讲的是jQuery里的ajax发送data的三种方式,利用ajax发送数据的好处是把数据发送到了servlet后,当前页面不进行跳转. jQuery的里的ajax发送data的方式主要有三种, ...
- ThreadLocal梳理
ThreadLocal ThreadLocal提供了线程独有的局部变量,可以在整个线程存活的过程中随时取用,极大地方便了一些逻辑的实现. 当使用ThreadLocal维护变量时,ThreadLocal ...
- 尝试用 Python 写了个病毒传播模拟程序
病毒扩散仿真程序,用 python 也可以. 概述 事情是这样的,B 站 UP 主 @ele 实验室,写了一个简单的疫情传播仿真程序,告诉大家在家待着的重要性,视频相信大家都看过了,并且 UP 主也放 ...
- ubuntu 如何添加alias
公司的nx 上面一般使用gvim 编辑文件.并且为gvim 增加了alias,只要敲 g 就是gvim 的意思,这样编辑一个文件只需要 g xxx.v 就可以了.非常方便. 在自己电脑上安装了ubun ...
- hdu 1159求最长公共子序列
题目描述:给出两个字符串,求两个字符串的公共子序列(不是公共子串,不要求连续,但要符合在原字符串中的顺序) in: abcfbc abfcab programming contest abcd mnp ...