千家信息网

spark 3.0.1集成delta 0.7.0之delta如何进行DDL操作

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,小编给大家分享一下spark 3.0.1集成delta 0.7.0之delta如何进行DDL操作,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我
千家信息网最后更新 2026年11月03日spark 3.0.1集成delta 0.7.0之delta如何进行DDL操作

小编给大家分享一下spark 3.0.1集成delta 0.7.0之delta如何进行DDL操作,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

分析

delta在0.7.0以前是不能够进行save表操作的,只能存储到文件中,也就是说他的元数据是和spark的其他元数据是分开的,delta是独立存在的,也是不能和其他表进行关联操作的,只有到了delta 0.7.0版本以后,才真正意义上和spark进行了集成,这也得益于spark 3.x的Catalog plugin API 特性。
还是先从delta的configurate sparksession入手,如下:

import org.apache.spark.sql.SparkSessionval spark = SparkSession  .builder()  .appName("...")  .master("...")  .config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension")  .config("spark.sql.catalog.spark_catalog", "org.apache.spark.sql.delta.catalog.DeltaCatalog")  .getOrCreate()

对于第二个配置 config("spark.sql.catalog.spark_catalog", "org.apache.spark.sql.delta.catalog.DeltaCatalog") 从spark configuration,我们可以看到对该spark.sql.catalog.spark_catalog的解释是

A catalog implementation that will be used as the v2 interface to Spark's built-in v1 catalog: spark_catalog. This catalog shares its identifier namespace with the spark_catalog and must be consistent with it; for example, if a table can be loaded by the spark_catalog, this catalog must also return the table metadata. To delegate operations to the spark_catalog, implementations can extend 'CatalogExtension'.

也就是说,通过该配置可以实现元数据的统一性,其实这也是spark社区和delta社区进行交互的一种结果

spark 3.x的Catalog plugin API

为了能搞懂delta为什么能够进行DDL和DML操作,就得先知道spark 3.x的Catalog plugin机制SPARK-31121.

首先是interface CatalogPlugin,该接口是catalog plugin的顶级接口,正如注释所说:

 * A marker interface to provide a catalog implementation for Spark. * 

* Implementations can provide catalog functions by implementing additional interfaces for tables, * views, and functions. *

* Catalog implementations must implement this marker interface to be loaded by * {@link Catalogs#load(String, SQLConf)}. The loader will instantiate catalog classes using the * required public no-arg constructor. After creating an instance, it will be configured by calling * {@link #initialize(String, CaseInsensitiveStringMap)}. *

* Catalog implementations are registered to a name by adding a configuration option to Spark: * {@code spark.sql.catalog.catalog-name=com.example.YourCatalogClass}. All configuration properties * in the Spark configuration that share the catalog name prefix, * {@code spark.sql.catalog.catalog-name.(key)=(value)} will be passed in the case insensitive * string map of options in initialization with the prefix removed. * {@code name}, is also passed and is the catalog's name; in this case, "catalog-name".

可以通过spark.sql.catalog.catalog-name=com.example.YourCatalogClass集成到spark中
该类的实现还可以集成其他额外的tables views functions的接口,这里就得提到接口TableCatalog,该类提供了与tables相关的方法:

/**   * List the tables in a namespace from the catalog.   * 

* If the catalog supports views, this must return identifiers for only tables and not views. * * @param namespace a multi-part namespace * @return an array of Identifiers for tables * @throws NoSuchNamespaceException If the namespace does not exist (optional). */ Identifier[] listTables(String[] namespace) throws NoSuchNamespaceException; /** * Load table metadata by {@link Identifier identifier} from the catalog. *

* If the catalog supports views and contains a view for the identifier and not a table, this * must throw {@link NoSuchTableException}. * * @param ident a table identifier * @return the table's metadata * @throws NoSuchTableException If the table doesn't exist or is a view */ Table loadTable(Identifier ident) throws NoSuchTableException;

这样就可以基于TableCatalog开发自己的catalog,从而实现multi-catalog support

还得有个接口DelegatingCatalogExtension,这是个实现了CatalogExtension接口的抽象类,而CatalogExtension继承了TableCatalog, SupportsNamespaces。DeltaCatalog实现了DelegatingCatalogExtension ,这部分后续进行分析。
最后还有一个class CatalogManager,这个类是用来管理CatalogPlugins的,且是线程安全的:

/** * A thread-safe manager for [[CatalogPlugin]]s. It tracks all the registered catalogs, and allow * the caller to look up a catalog by name. * * There are still many commands (e.g. ANALYZE TABLE) that do not support v2 catalog API. They * ignore the current catalog and blindly go to the v1 `SessionCatalog`. To avoid tracking current * namespace in both `SessionCatalog` and `CatalogManger`, we let `CatalogManager` to set/get * current database of `SessionCatalog` when the current catalog is the session catalog. */// TODO: all commands should look up table from the current catalog. The `SessionCatalog` doesn't//       need to track current database at all.private[sql]class CatalogManager(    conf: SQLConf,    defaultSessionCatalog: CatalogPlugin,    val v1SessionCatalog: SessionCatalog) extends Logging {

我们看到CatalogManager管理了v2版本的 CatalogPlugin和v1版本的sessionCatalog,这个是因为历史的原因导致必须得兼容v1版本

那CatalogManager在哪里被调用呢。 我们看一下BaseSessionStateBuilder ,可以看到该类中才是正宗使用CatalogManager的地方:

/**   * Catalog for managing table and database states. If there is a pre-existing catalog, the state   * of that catalog (temp tables & current database) will be copied into the new catalog.   *   * Note: this depends on the `conf`, `functionRegistry` and `sqlParser` fields.   */  protected lazy val catalog: SessionCatalog = {    val catalog = new SessionCatalog(      () => session.sharedState.externalCatalog,      () => session.sharedState.globalTempViewManager,      functionRegistry,      conf,      SessionState.newHadoopConf(session.sparkContext.hadoopConfiguration, conf),      sqlParser,      resourceLoader)    parentState.foreach(_.catalog.copyStateTo(catalog))    catalog  }  protected lazy val v2SessionCatalog = new V2SessionCatalog(catalog, conf)  protected lazy val catalogManager = new CatalogManager(conf, v2SessionCatalog, catalog)

SessionCatalog 是v1版本的,主要是跟底层的元数据存储通信,以及管理临时视图,udf的,这一部分暂时不分析,重点放到v2版本的sessionCatalog, 我们看一下V2SessionCatalog:

/** * A [[TableCatalog]] that translates calls to the v1 SessionCatalog. */class V2SessionCatalog(catalog: SessionCatalog, conf: SQLConf)  extends TableCatalog with SupportsNamespaces {  import org.apache.spark.sql.connector.catalog.CatalogV2Implicits.NamespaceHelper  import V2SessionCatalog._  overrideval defaultNamespace: Array[String] = Array("default")  override def name: String = CatalogManager.SESSION_CATALOG_NAME  // This class is instantiated by Spark, so `initialize` method will not be called.  override def initialize(name: String, options: CaseInsensitiveStringMap): Unit = {}  override def listTables(namespace: Array[String]): Array[Identifier] = {    namespace match {      case Array(db) =>        catalog          .listTables(db)          .map(ident => Identifier.of(Array(ident.database.getOrElse("")), ident.table))          .toArray      case _ =>        throw new NoSuchNamespaceException(namespace)    }  }

我们分析一下listTables方法可知,v2的sessionCatalog操作 都是委托给了v1版本的sessionCatalog去操作的,其他的方法也是一样, 而且name默认为CatalogManager.SESSION_CATALOG_NAME,也就是spark_catalog,这里后面也会提到,注意一下。 而且,catalogmanager在逻辑计划中的分析器和优化器中也会用到,因为会用到其中的元数据:

protected def analyzer: Analyzer = new Analyzer(catalogManager, conf) {...protected def optimizer: Optimizer = {    new SparkOptimizer(catalogManager, catalog, experimentalMethods) {      override def earlyScanPushDownRules: Seq[Rule[LogicalPlan]] =        super.earlyScanPushDownRules ++ customEarlyScanPushDownRules      override def extendedOperatorOptimizationRules: Seq[Rule[LogicalPlan]] =        super.extendedOperatorOptimizationRules ++ customOperatorOptimizationRules    }  }

而analyzer和optimizer正是spark sql进行解析的核心中的核心,当然还有物理计划的生成。 那这些analyzer和optimizer是在哪里被调用呢?
我们举一个例子,DataSet中的filter方法就调用了:

 */  def filter(conditionExpr: String): Dataset[T] = {    filter(Column(sparkSession.sessionState.sqlParser.parse_Expression(conditionExpr)))  }

sessionState.sqlParser就是刚才所说的sqlParser:

protected lazy val sqlParser: ParserInterface = {    extensions.buildParser(session, new SparkSqlParser(conf))  }

只有整个逻辑 从sql解析到使用元数据的数据链路,我们就能大致知道怎么一回事了。

delta的DeltaCatalog

我们回过头来看看,delta的DeltaCatalog是怎么和spark 3.x进行结合的 ,上源码DeltaCatalog:

class DeltaCatalog(val spark: SparkSession) extends DelegatingCatalogExtension  with StagingTableCatalog  with SupportsPathIdentifier {  def this() = {    this(SparkSession.active)  }  ...

就如之前所说的DeltaCatalog继承了DelegatingCatalogExtension,从名字可以看出这是一个委托类,那到底是怎么委托的呢以及委托给谁呢?

public abstract class DelegatingCatalogExtension implements CatalogExtension {  private CatalogPlugin delegate;  public final void setDelegateCatalog(CatalogPlugin delegate) {    this.delegate = delegate;  }

该类中有个setDelegateCatalog方法,该方法在CatalogManager中的loadV2SessionCatalog方法中被调用:

private def loadV2SessionCatalog(): CatalogPlugin = {    Catalogs.load(SESSION_CATALOG_NAME, conf) match {      case extension: CatalogExtension =>        extension.setDelegateCatalog(defaultSessionCatalog)        extension      case other => other    }  }

而该方法被v2SessionCatalog调用:

private[sql] def v2SessionCatalog: CatalogPlugin = {    conf.getConf(SQLConf.V2_SESSION_CATALOG_IMPLEMENTATION).map { customV2SessionCatalog =>      try {        catalogs.getOrElseUpdate(SESSION_CATALOG_NAME, loadV2SessionCatalog())      } catch {        case NonFatal(_) =>          logError(            "Fail to instantiate the custom v2 session catalog: " + customV2SessionCatalog)          defaultSessionCatalog      }    }.getOrElse(defaultSessionCatalog)  }

这个就是返回默认的v2版本的SessionCatalog实例,分析一下这个方法:

   首先得到配置项SQLConf.V2_SESSION_CATALOG_IMPLEMENTATION,也就是spark.sql.catalog.spark_catalog配置,   如果spark配置了的话,就调用loadV2SessionCatalog加载该类,,否则就加载默认的v2SessionCatalog,也就是V2SessionCatalog实例

这里我们就发现了:
delta配置的spark.sql.catalog.spark_catalog为"org.apache.spark.sql.delta.catalog.DeltaCatalog",也就是说,spark中的V2SessionCatalog是DeltaCatalog的实例,而DeltaCatalog的委托给了BaseSessionStateBuilder中的V2SessionCatalog实例。

具体看看DeltaCatalog 的createTable方法,其他的方法类似:

override def createTable(      ident: Identifier,      schema: StructType,      partitions: Array[Transform],      properties: util.Map[String, String]): Table = {    if (DeltaSourceUtils.isDeltaDataSourceName(getProvider(properties))) {      createDeltaTable(        ident, schema, partitions, properties, sourceQuery = None, TableCreationModes.Create)    } else {      super.createTable(ident, schema, partitions, properties)    }  }...private def createDeltaTable(      ident: Identifier,      schema: StructType,      partitions: Array[Transform],      properties: util.Map[String, String],      sourceQuery: Option[LogicalPlan],      operation: TableCreationModes.CreationMode): Table = {     ...    val tableDesc = new CatalogTable(      identifier = TableIdentifier(ident.name(), ident.namespace().lastOption),      tableType = tableType,      storage = storage,      schema = schema,      provider = Some("delta"),      partitionColumnNames = partitionColumns,      bucketSpec = maybeBucketSpec,      properties = tableProperties.toMap,      comment = Option(properties.get("comment")))    // END: copy-paste from the super method finished.    val withDb = verifyTableAndSolidify(tableDesc, None)    ParquetSchemaConverter.checkFieldNames(tableDesc.schema.fieldNames)    CreateDeltaTableCommand(      withDb,      getExistingTableIfExists(tableDesc),      operation.mode,      sourceQuery,      operation,      tableByPath = isByPath).run(spark)    loadTable(ident)      } override def loadTable(ident: Identifier): Table = {    try {      super.loadTable(ident) match {        case v1: V1Table if DeltaTableUtils.isDeltaTable(v1.catalogTable) =>          DeltaTableV2(            spark,            new Path(v1.catalogTable.location),            catalogTable = Some(v1.catalogTable),            tableIdentifier = Some(ident.toString))        case o => o      }      }
  • 判断是否是delta数据源,如果是的话,跳到createDeltaTable方法,否则直接调用super.createTable方法,

  • createDeltaTable先会进行delta特有的CreateDeltaTableCommand.run()命令写入delta数据,之后载loadTable

  • loadTable则会调用super的loadTable,而方法会调用V2SessionCatalog的loadTable,而V2SessionCatalog最终会调用v1版本sessionCatalog的getTableMetadata方法,从而组成V1Table(catalogTable)返回,这样就把delta的元数据信息持久化到了v1 SessionCatalog管理的元数据库中

  • 如果不是delta数据源,则调用super.createTable方法,该方法调用V2SessionCatalog的createTable,而最终还是调用v1版本sessionCatalog的createTable方法

我们这里重点分析了delta数据源到元数据的存储,非delta数据源的代码就没有粘贴过来,有兴趣的自己可以编译源码跟踪一下

我们还得提一下spark.sql.defaultCatalog的默认配置为spark_catalog,也就是sql的默认catalog为spark_catalog,对应到delta的话就是DeltaCatalog。

以上是"spark 3.0.1集成delta 0.7.0之delta如何进行DDL操作"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

方法 数据 版本 也就是 分析 配置 接口 委托 实例 数据源 管理 也就是说 就是 所说 篇文章 存储 内容 只有 核心 源码 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技公司经营范围 科技赋能下的互联网金融 新华互联网科技学院 平安科技 互联网 上海喔噻互联网科技 新浪科技互联网 青海省互联网新科技 富国互联网科技创新基金 青岛中康互联网医疗科技有限公司 移动互联网科技股 电子商务与互联网科技的关系 35岁以上互联网科技行业 互联网科技包装股份有限公司上市 福州德业互联网科技 全球科技与互联网公司市值变化 中国科技和美国互联网 长沙易易互联网科技有限公司 重庆科技产业互联网有哪些 科技互联网企业在行动 广州全民伙伴互联网科技有限公司 广州万易互联网科技有限公司 腾讯阿里互联网科技 互联网高科技企业 联恩商钥互联网科技有限公司 互联网数据科技专业 国大互联网教育科技有限公司薪资 银川新华互联网科技学校原画 悟空问答互联网科技 互联网科技大会大佬 青岛互联网科技园
0