2.2 语义分析与Metastore


2.2 语义分析与 Metastore

本节摘要:语义分析是编译流水线的第二关:Driver 拿着语法树逐项核对事实——表是否存在、列在哪张表上、类型是否兼容、分区值是否合法。所有核对都依赖 Metastore 提供的元数据。本节讲清核对清单、类型系统的隐式转换规则、视图在语义层的展开,以及一次 Driver 与 Metastore 的完整 RPC 时序。

话通顺之后,还要事属实

上一节的结尾留了个悬念:语法分析产出的 AST 里,orders 只是一个名字。接下来 Driver 要把每个名字换成真实对象,这个过程叫语义分析。它像译员拿到稿件后先查专业词典:人名、地名、术语一个个核对,对不上的稿件直接退回,绝不带着错误下车间。

对一条普通查询,语义关要核对的项目比想象中多:

  • 库与表的存在性:USE 的库存在吗?FROM 与 JOIN 里的每张表存在吗?当前用户有权限读吗?
  • 列的可解析性:每个列引用能唯一落到某张表(或别名)上吗?SELECT 里出现 region,而 FROM 的两张表都有 region 列且未加限定,就死在这一步;
  • 类型兼容性:比较与运算两侧的类型能对上吗?对不上时按什么规则隐式转换?
  • 函数解析:SUM 是内置聚合吗?参数类型合法吗?如果是临时 UDF,当前会话注册过吗?
  • 分区合法性:分区列引用有效吗?静态写入的分区值符合类型吗?
  • 别名展开:表别名与列别名按作用域正确替换;
  • 视图展开:查询里用到视图的,把视图定义展开进来,当成子查询处理。

这些核对全部依赖 Metastore。Driver 在编译期会发起一串 Thrift RPC:getDatabase、getTable、getPartitions 或按前缀 listPartitionsByFilter 等。对大分区表,这一步的耗时不可忽视——百万分区的表,光拿分区清单就可能拖慢编译十几秒,这也是第 8 章讲分区数量治理的原因之一。

Driver 与 Metastore 的编译期时序

注意时序图最后一行:编译期 Driver 只碰元数据,不碰数据文件。所以"表元数据在但 HDFS 文件被误删"的表,编译照样通过、EXPLAIN 照样出计划,任务跑起来才报找不到文件。反过来,"元数据被删但数据还在"的表,查询直接死在语义关——两种"表坏了"的修法完全不同,前者修数据,后者要靠备份或 msck 重建分区。

类型系统:隐式转换的秩序

语义关里最容易踩坑的是类型核对。Hive 的类型分族:数值族(TINYINT、INT、BIGINT、DECIMAL、DOUBLE)、字符族(STRING、VARCHAR、CHAR)、时间族(TIMESTAMP、DATE)、复合类型(ARRAY、MAP、STRUCT)。

比较运算两侧类型不一致时,Hive 按"族内提升、跨族转字符串慎用"的思路做隐式转换:

SELECT * FROM orders WHERE customer_id = 1001;

如果 customer_id 是 STRING,字面量 1001 会被转成字符串再比较,语义上没错。但下面这个就危险了:

SELECT * FROM events WHERE event_ts = 1767139200;

event_ts 若是 STRING 类型的 '2026-01-01 00:00:00',这条件永远为假——字符串与数字比较时把字段转成 DOUBLE,日期文本转出来是 0 或不可比,查询安静地返回空集,不报任何错。语义关只拦"转不了",不拦"转了但含义荒谬"。生产上的纪律是:等值比较两侧显式同型,时间戳比较永远 CAST 或改写为字符串字面量。

DECIMAL 与 DOUBLE 的选择也在这关有回响。金额列用 DECIMAL(10,2) 是行业共识,SUM 之后精度受控;若用 DOUBLE,聚合时浮点误差会在对账时冒头。语义分析不会替你做这个决定,它只保证你写下的类型组合能编译通过。

视图:语义层的文本替换

视图在语义分析阶段被展开。你查询一个视图,Driver 取回视图的定义语句,把它当作子查询嵌进你的 AST,再做全套核对。由此推出视图的三个工程特性:

第一,视图不存数据。它是编译期的宏,查询视图的执行计划等价于查询其定义展开后的计划——EXPLAIN 一个视图查询,你在输出里看到的是底层表的 TableScan,找不到任何"视图算子"。

第二,视图是逻辑重组工具,不是性能工具。它能让复杂 JOIN 的口径固化为一个名字,让下游统一取数口径;但它不缓存结果。要缓存,得用物化视图(Hive 3 的能力,第 7 章提)或干脆 INSERT 到汇总表。

第三,视图有编译期绑定风险。底层表加了列,视图定义若是 SELECT 星号则含义漂移;底层表改了列名,视图直接编译失败。核心视图纳入变更管理,是数仓治理的基本动作。

语义关的报错速查

把这一关的常见报错列成清单,排错时先归类再动手:

报错关键词 含义 常见原因
Invalid table 或 Database not found 表或库不存在 库名前缀错、库未创建、Metastore 连接错环境
Column cannot be resolved 列解析失败 列名拼错、多表同名列未加限定、别名作用域错误
cannot be applied to 类型不兼容 函数参数类型错误、比较两侧无法转换
Both left and right aliases JOIN 条件引用了不在此侧的别名 复杂 JOIN 的 ON 条件写错侧
Partition not found 静态分区值不存在或非法 分区值类型或格式与定义不符

一个实用习惯:把 SemanticException 类报错当作"词典核对失败"处理,先核对三件事——连的哪个环境、哪个库、表结构最近有没有变更。三成以上的"昨天还能跑"问题,根源是连错了环境或表被重建。

语义分析的交付物

核对全部通过后,Driver 把 QB 里的名字全部替换成带类型、带来源的真实对象:每个列引用带上表限定与类型,每个函数绑定到具体实现,每个分区谓词换算成 Metastore 返回的分区目录清单。这份"事实核对完毕"的查询块,就是下一节逻辑计划的原料——算子树的每个节点,都能在 QB 的槽位里找到出处。

语义关把名字换成对象。下一节进入流水线最精彩的一段:优化器拿到对象齐全的查询块,开始在算子树上做手术。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U