sparksql源码系列 | 一文搞懂Show create table 执行原理

2022-06-09 21:32:13 浏览数 (1)

这篇文章主要介绍了show create table命令执行的源码流程,弄清楚了sparksql是怎么和hive元数据库交互,查询对应表的metadata,然后拼接成最终的结果展示给用户的。

今天这篇文章也是来自于【源码共读群】的一个讨论,先上聊天:

我们平时都很关注select这样的查询语句,却很少关注show create table 这样的语句的执行过程,在网上确实也很难搜到写相关内容的博客。正好借这个问题,深挖一下运行原理,于是,花2个小时,撸一遍源码,得到了基本的结论:

哈哈,感谢大家认可啦,群友都希望录个视频,那我先写文章,然后,再录个短屏。

下面开挖,源码是枯燥的,但也是我们能看到真相的窗口~~

本文基于spark 3.2

本文大纲

1、写能模拟从hive查表的本地测试类

1、写能模拟从hive查表的本地测试类

我们在读sparksql源码时,为了方便,基本上都是用df.createOrReplaceTempView("XXX")这样的形式,来产生一些数据,这些足够我们去研究90%以上的规则,但这些不能模拟hive的情况,如果我们搭建远程连hive的环境,又会花费大量的精力。

还好,在sparksql源码工程里,我们可以通过继承TestHiveSingleton,在不用搭建hive环境的情况下,来模拟hive。

这个在【源码共读】的分享上我们会专门讲~~

测试类代码如下:

2、hive中的实体类和元数据库表及字段的对应关系

MTable(类)--> TBLS(表)

MDatabase(类)-->DBS(表)

MStorageDescriptor(类)-->SDS(表)

MFieldSchema(类)-->TYPE_FIELDS(表)

partitionKeys(MTable类中的filed) -->PARTITION_KEYS(表)

parameters (MTable类中的filed--> TABLE_PARAMS(表)

下面的配制包含了类中的字段及表字段的对应关系:

代码语言:javascript复制
<class name="MTable" table="TBLS" identity-type="datastore" detachable="true">
  <datastore-identity>
    <column name="TBL_ID"/>
  </datastore-identity>
  <index name="UniqueTable" unique="true">
    <column name="TBL_NAME"/>
    <column name="DB_ID"/>
  </index>
  <field name="tableName">
    <column name="TBL_NAME" length="256" jdbc-type="VARCHAR"/>
  </field>
  <field name="database">
    <column name="DB_ID"/>
  </field>
  <field name="partitionKeys" table="PARTITION_KEYS" >
    <collection element-type="MFieldSchema"/>
    <join>
      <primary-key name="PARTITION_KEY_PK">
        <column name="TBL_ID"/>
        <column name="PKEY_NAME"/>
      </primary-key>
      <column name="TBL_ID"/>
    </join>
    <element>
      <embedded>
        <field name="name">
          <column name="PKEY_NAME" length="128" jdbc-type="VARCHAR"/>
          </field>
        <field name="type">
          <column name="PKEY_TYPE" length="767" jdbc-type="VARCHAR" allows-null="false"/>
        </field>
        <field name="comment" >
          <column name="PKEY_COMMENT" length="4000" jdbc-type="VARCHAR" allows-null="true"/>
        </field>
      </embedded>
    </element>
  </field>
  <field name="sd" dependent="true">
    <column name="SD_ID"/>
  </field>
  <field name="owner">
    <column name="OWNER" length="767" jdbc-type="VARCHAR"/>
  </field>
  <field name="createTime">
    <column name="CREATE_TIME" jdbc-type="integer"/>
  </field>
  <field name="lastAccessTime">
    <column name="LAST_ACCESS_TIME" jdbc-type="integer"/>
  </field>
  <field name="retention">
    <column name="RETENTION" jdbc-type="integer"/>
  </field>
  <field name="parameters" table="TABLE_PARAMS">
    <map key-type="java.lang.String" value-type="java.lang.String"/>
    <join>
      <column name="TBL_ID"/>
    </join>
    <key>
       <column name="PARAM_KEY" length="256" jdbc-type="VARCHAR"/>
    </key>
    <value>
       <column name="PARAM_VALUE" length="32672" jdbc-type="VARCHAR"/>
    </value>
  </field>
  <field name="viewOriginalText" default-fetch-group="false">
    <column name="VIEW_ORIGINAL_TEXT" jdbc-type="LONGVARCHAR"/>
  </field>
  <field name="viewExpandedText" default-fetch-group="false">
    <column name="VIEW_EXPANDED_TEXT" jdbc-type="LONGVARCHAR"/>
  </field>
  <field name="rewriteEnabled">
    <column name="IS_REWRITE_ENABLED"/>
  </field>
  <field name="tableType">
    <column name="TBL_TYPE" length="128" jdbc-type="VARCHAR"/>
  </field>
</class>

3、源码分析执行过程

通过println,输出 show create table orders 的物理执行计划,可看到,真正执行的是ShowCreateTableCommand这个类。

代码流程:

两个核心方法:

查hive元数据库(ObjectStore.getMTable)
  • mtbl = (MTable) query.execute(table, db)对应的sql:

获取表的一些基本信息(tbl_id, tbl_type等)

代码语言:javascript复制
SELECT
   DISTINCT 'org.apache.hadoop.hive.metastore.model.MTable' AS NUCLEUS_TYPE,
  A0.CREATE_TIME,
  A0.LAST_ACCESS_TIME,
  A0.OWNER,
  A0.RETENTION,
  A0.IS_REWRITE_ENABLED,
  A0.TBL_NAME,
  A0.TBL_TYPE,
  A0.TBL_ID 
FROM
  TBLS A0
  LEFT OUTER JOIN DBS B0 ON A0.DB_ID = B0.DB_ID
WHERE
  A0.TBL_NAME = ?
  AND B0."NAME" = ?

debug中的sql:

sql字段和实体类的对应关系:

debug的过程如下:

可以看到执行过该方法后,一些基本字段被填充上了

  • pm.retrieve(mtbl)对应的sql:

获得database(MDatabase),sd(MStorageDescriptor),parameters,partitionKeys

代码语言:javascript复制
SELECT
  B0."DESC",
  B0.DB_LOCATION_URI,
  B0."NAME",
  B0.OWNER_NAME,
  B0.OWNER_TYPE,
  B0.DB_ID,
  C0.INPUT_FORMAT,
  C0.IS_COMPRESSED,
  C0.IS_STOREDASSUBDIRECTORIES,
  C0.LOCATION,
  C0.NUM_BUCKETS,
  C0.OUTPUT_FORMAT,
  C0.SD_ID,
  A0.VIEW_EXPANDED_TEXT,
  A0.VIEW_ORIGINAL_TEXT
FROM
  TBLS A0
  LEFT OUTER JOIN DBS B0 ON A0.DB_ID = B0.DB_ID
  LEFT OUTER JOIN SDS C0 ON A0.SD_ID = C0.SD_ID
WHERE
  A0.TBL_ID = ?

debug中的sql:

sql字段和实体类的对应关系:

debug的过程如下:

真正计算parameters,partitionKeys时,还会再经过一次回调,才能获取字段的schema:

依据的hive元数据信息,生成最终要展示的内容 (ShowCreateTableCommand.run )
代码语言:javascript复制

private def showCreateDataSourceTable(metadata: CatalogTable, builder: StringBuilder): Unit = {
  //colums
  showDataSourceTableDataColumns(metadata, builder)
  //table的参数:存储格式等
  showDataSourceTableOptions(metadata, builder)
  showDataSourceTableNonDataColumns(metadata, builder)
  //table的注释
  showTableComment(metadata, builder)
  //location
  showTableLocation(metadata, builder)
  //比如:TBLPROPERTIES
  showTableProperties(metadata, builder)
}

最后拼接的结果:

代码语言:javascript复制
CREATE TABLE `default`.`orders` (
   n `id` INT,
   n `make` STRING,
   n `type` STRING,
   n `price` INT,
   n `pdate` STRING,
   n `customer` STRING,
   n `city` STRING,
   n `state` STRING,
   n `month` INT
)  nUSING parquet  nPARTITIONED BY (state, month)  nTBLPROPERTIES ( n 'transient_lastDdlTime' = '1651553453')  n

0 人点赞