3. 元数据基础操作:DESCRIBE DETAIL、SHOW COLUMNS 与 DESCRIBE HISTORY
好,咱们正式开始动手操作了。这一章我带你过三个最常用的元数据查看命令。说白了,就是让你学会怎么「看」Delta Lake 表的内在。
我个人习惯,拿到一张新表,第一件事不是急着查数据,而是先看看它的元数据。就像你租房子,总得先看看户型图、水电表、历史记录吧?Delta Lake 也一样。
核心知识点速览
- DESCRIBE DETAIL — 查看表的物理元数据(大小、文件数、分区等)
- SHOW COLUMNS — 查看表的列名、类型、注释
- DESCRIBE HISTORY — 查看表的事务历史(谁、什么时候、做了什么)
3.1 DESCRIBE DETAIL — 一眼看穿表的「底细」
这个命令,是我用得最频繁的。它返回一张表的完整元数据快照。包括多少文件、多大、有没有分区、Delta 版本号等等。
语法很简单:
DESCRIBE DETAIL table_name;
或者用 Spark SQL 的方式:
DESCRIBE DETAIL delta.`/path/to/table`;
来看个实际例子。假设我们有一张用户行为表 user_events:
DESCRIBE DETAIL user_events;
返回的结果大概长这样:
| 列名 | 说明 | 示例值 |
|---|---|---|
| format | 存储格式 | delta |
| id | 表 ID(UUID) | 123e4567-e89b-12d3-a456-426614174000 |
| name | 表名 | user_events |
| description | 表注释 | 用户行为日志表 |
| location | 数据存储路径 | /data/delta/user_events |
| createdAt | 创建时间 | 2024-01-15 10:30:00 |
| lastModified | 最后修改时间 | 2024-03-20 14:22:35 |
| partitionColumns | 分区列 | ["dt"] |
| numFiles | 数据文件数 | 128 |
| sizeInBytes | 数据总大小(字节) | 5368709120 |
| properties | 表属性(JSON) | {"delta.autoOptimize": "true"} |
| minReaderVersion | 最低读取协议版本 | 1 |
| minWriterVersion | 最低写入协议版本 | 2 |
💡 我的小技巧
我经常用 DESCRIBE DETAIL 来快速判断一张表是否需要优化。比如看到 numFiles 特别多但 sizeInBytes 不大,说明小文件太多,该做 OPTIMIZE 了。我在项目中遇到过一张表,文件数 2000+,总大小才 2GB,跑个查询慢得要命。一查 DETAIL,问题一目了然。
3.2 SHOW COLUMNS — 看清表的「骨架」
这个命令更简单,就是列出表的所有列。语法:
SHOW COLUMNS IN table_name;
或者:
SHOW COLUMNS FROM table_name;
举个例子:
SHOW COLUMNS IN user_events;
返回结果:
| col_name | data_type | comment |
|---|---|---|
| user_id | string | 用户 ID |
| event_type | string | 事件类型 |
| event_time | timestamp | 事件发生时间 |
| page_url | string | 页面 URL |
| dt | string | 分区日期 |
嗯,这里要注意一点:SHOW COLUMNS 不会显示分区信息。想看分区列,得用 DESCRIBE DETAIL 或者 SHOW PARTITIONS。
⚠️ 避坑指南
我曾经在排查一个数据写入问题时,用 SHOW COLUMNS 发现目标表少了一列。原来上游改了 schema,但下游表没同步更新。所以,每次 schema 变更后,记得用 SHOW COLUMNS 确认一下。别想当然。
3.3 DESCRIBE HISTORY — 翻看表的「操作日志」
这是 Delta Lake 最强大的特性之一。每一张 Delta 表都记录了完整的操作历史。语法:
DESCRIBE HISTORY table_name;
默认返回最近 30 条操作记录。你也可以指定数量:
DESCRIBE HISTORY table_name LIMIT 10;
来看个实际输出:
| version | timestamp | operation | operationParameters | userName | readVersion | isolationLevel | isBlindAppend |
|---|---|---|---|---|---|---|---|
| 5 | 2024-03-20 14:22:35 | WRITE | {"mode":"Append","partitionBy":"[]"} | alice@company.com | 4 | SnapshotIsolation | true |
| 4 | 2024-03-19 09:15:42 | DELETE | {"predicate":"[\"(event_type = 'click')\"]"} | bob@company.com | 3 | Serializable | false |
| 3 | 2024-03-18 16:00:10 | UPDATE | {"predicate":"(user_id = '12345')"} | alice@company.com | 2 | Serializable | false |
| 2 | 2024-03-17 11:30:00 | MERGE | {"predicate":"(user_events.user_id = updates.user_id)"} | system | 1 | Serializable | false |
| 1 | 2024-03-16 08:00:00 | CREATE TABLE | {"isManaged":"true","description":null,"partitionBy":"[\"dt\"]","properties":"{}"} | admin@company.com | 0 | Serializable | true |
你看,每一行都记录了:谁、在什么时间、做了什么操作、基于哪个版本、用了什么隔离级别。这简直就是数据血缘的「黑匣子」。
🔍 关键字段解读
- version — 版本号,从 0 开始递增
- operation — 操作类型:CREATE TABLE、WRITE、DELETE、UPDATE、MERGE、OPTIMIZE、VACUUM 等
- operationParameters — 操作参数,比如写入模式、删除条件
- readVersion — 读取的基版本(用于时间旅行)
- isBlindAppend — 是否为纯追加操作(true 表示没有修改已有数据)
💡 实际场景
有一次,业务方说某天的数据不对。我直接 DESCRIBE HISTORY 一看,发现那天凌晨有个 DELETE 操作,删掉了一部分数据。再查 operationParameters,发现删除条件写错了。嗯,这就是历史记录的威力——你永远可以找到「谁动了我的数据」。
3.4 三个命令的对比总结
最后,我用一张表帮你理清这三个命令的定位:
| 命令 | 关注点 | 典型用途 | 返回行数 |
|---|---|---|---|
| DESCRIBE DETAIL | 表的物理属性 | 检查文件数、大小、分区、协议版本 | 1 行 |
| SHOW COLUMNS | 表的逻辑结构 | 查看列名、类型、注释 | 每列 1 行 |
| DESCRIBE HISTORY | 表的操作记录 | 排查数据变更、审计、时间旅行 | 每操作 1 行 |
说白了,这三个命令就是你的「元数据三件套」。拿到一张新表,先 DESCRIBE DETAIL 看底细,再 SHOW COLUMNS 看结构,如果怀疑数据有问题,DESCRIBE HISTORY 翻旧账。这套组合拳打下来,表里有什么猫腻都藏不住。