spark写表指定外部表_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 classDropTableCommand(

tableName: TableIdentifier,

ifExists: Boolean,

isView: Boolean,

purge: Boolean,

withData:Boolean= false //TODO 外部表是否需要删除表数据

) extends RunnableCommand

DropTableCommand本质上其实是用了command设计模式,实际在运行时,会调用其run方法,修改 run 方法,如下:

1 override def run(sparkSession: SparkSession): Seq[Row] ={2 val catalog =sparkSession.sessionState.catalog3 val isTempView =catalog.isTemporaryTable(tableName)4

5 if (!isTempView &&catalog.tableExists(tableName)) {6 //If the command DROP VIEW is to drop a table or DROP TABLE is to drop a view7 //issue an exception.

8 catalog.getTableMetadata(tableName).tableType match {9 case CatalogTableType.VIEW if !isView =>

10 throw newAnalysisException(11 "Cannot drop a view with DROP TABLE. Please use DROP VIEW instead")12 case o if o != CatalogTableType.VIEW && isView =>

13 throw newAnalysisException(14 s"Cannot drop a table with DROP VIEW. Please use DROP TABLE instead")15 case _ =>

16 }17 }18

19 if (isTempView ||catalog.tableExists(tableName)) {20 try{21 sparkSession.sharedState.cacheManager.uncacheQuery(22 sparkSession.table(tableName), cascade = !isTempView)23 } catch{24 case NonFatal(e) =>log.warn(e.toString, e)25 }26 catalog.refreshTable(tableName)27 log.warn(s"withData:${withData}")28 catalog.dropTable(tableName, ifExists, purge, withData)29 } else if(ifExists) {30 //no-op

31 } else{32 throw new AnalysisException(s"Table or view not found: ${tableName.identifier}")33 }34 Seq.empty[Row]35 }

在第 28 行,为 catalog对象的dropTable 添加 withData 参数。其中catalog是 org.apache.spark.sql.catalyst.catalog.SessionCatalog 的实例。其子类并没有重写其 dropTable 方法,故只需要修改其dropTable 方法即可。具体修改代码如下:

1 /**

2 * Drop a table.3 *4 * If a database is specified in `name`, this will drop the table from that database.5 * If no database is specified, this will first attempt to drop a temporary view with6 * the same name, then, if that does not exist, drop the table from the current database.7 */

8 def dropTable(9 name: TableIdentifier,10 ignoreIfNotExists: Boolean,11 purge: Boolean,12 withData:Boolean = false //外部表是否需要在hdfs上删除其对应的数据

13 ): Unit = synchronized{14 val db =formatDatabaseName(name.database.getOrElse(currentDb))15 val table =formatTableName(name.table)16 if (db ==globalTempViewManager.database) {17 val viewExists =globalTempViewManager.remove(table)18 if (!viewExists && !ignoreIfNotExists) {19 throw newNoSuchTableException(globalTempViewManager.database, table)20 }21 } else{22 if (name.database.isDefined || !tempViews.contains(table)) {23 requireDbExists(db)24 //When ignoreIfNotExists is false, no exception is issued when the table does not exist.25 //Instead, log it as an error message.

26 if(tableExists(TableIdentifier(table, Option(db)))) {27 logError(s"withData :${withData}")28 externalCatalog.dropTable(db, table, ignoreIfNotExists = true,purge =purge, withData)29 } else if (!ignoreIfNotExists) {30 throw new NoSuchTableException(db = db, table =table)31 }32 } else{33 tempViews.remove(table)34 }35 }36 }

为防止在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= newPath(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/make-distribution.sh --name 2.6.0-cdh5.14.0 --tgz --mvn /opt/soft/apache-maven-3.6.1/bin/mvn -Pyarn -Phadoop-2.6 -Phive -Phive-thriftserver -Dhadoop.version=2.6.0-cdh5.14.0 -X

注:由于编译的是 cdh版本,一些jar包不在中央仓库,在pom.xml文件中,添加 cloudera maven 源:

cloudera

https://repository.cloudera.com/artifactory/cloudera-repos

为了加快 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

2zhangsan4

3lisi5

4wangwu6

5rose7

6nose8

7info9

8test10

查看 hdfs 上对应目录是否有数据

[root@xxx ~]# hdfs dfs -ls -R /user/hive/warehouse/test.db/ext1-rwxr-xr-x 3 root supergroup 76 2020-02-27 15:58 /user/hive/warehouse/test.db/ext1/part-00000-aae237ac-4a0b-425c-a0f1-5d54d1e88957-c000

5.2 删除表

spark-sql> drop table ifexists ext1 with data;

5.3 验证表元数据已删除成功

spark-sql>show tables;

test personfalse

没有ext表,说明已删除成功。

5.4 验证hdfs上数据已删除成功

[root@node01 ~]# hdfs dfs -ls -R /user/hive/warehouse/test.db/ext1ls: `/user/hive/warehouse/test.db/ext1': No such file or directory

该目录已不存在,说明hdfs上数据已删除成功。

总结

本文具体介绍了如何修改spark sql 的源码,在删除external表时可选择地删除hdfs上的底层数据。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值