背景介绍
MaxCompute 支持外部表的元数据列(Metadata Column),允许在 SQL 查询中直接访问底层存储(特别是OSS)的文件级元数据,如文件大小、最后修改时间、文件路径等。用户可基于这些属性对数据进行过滤、排序或分析,扩展外部表的使用场景。
在 AI 数据预处理场景中,OSS 上的原始数据通常由业务系统自动生成,后续用于 AI 训练或调优。并非所有数据都需要参与训练,需按特定条件筛选。而筛选条件往往不是数据列的值,而是文件本身的属性,例如文件大小、生成时间或命名规范。元数据列功能允许按文件元信息(而非数据列)过滤数据,从而快速获取 AI 训练所需的数据集。
实现的核心是通过以下几方面增强现有架构:
添加 11 种预定义元数据列供查询。
增强 Hive 输入格式包装器,检测并处理元数据列。
集成 JindoOSS 文件系统以获取文件元数据。
优化 SQL 编译器,验证和解析元数据列。
支持的元数据列
元数据列不属于数据列,默认不会出现,也无法通过
SELECT *或其他通配符查询。需通过_***形式查询。元数据列后续会持续增加。
列名 | 类型 | 说明 | 默认值 |
| VARCHAR(2048) | 文件的完整 OSS 路径 | 默认为OSS路径: |
| VARCHAR(256) | 不含路径的文件基本名称 | OSS上的文件名,例如: |
| BIGINT | 文件大小(字节) | 无 |
| TIMESTAMP | 最后修改时间 | 无 |
| VARCHAR(64) | OSS ETag 签名信息 | 无 |
| VARCHAR(32) | 文件类型。取值为:
| FILE |
| VARCHAR(32) | 文件版本信息(可能为 NULL) | NULL |
| VARCHAR(32) | 文件所有者信息 | 无 |
| VARCHAR(32) | 存储类别。取值为:
| Standard |
| VARCHAR(32) | 存储策略。取值为:
| UNSPECIFIED |
| VARCHAR(32) | 文件系统状态。取值为:
| UNKNOWN |
使用限制
分区表: 不支持带元数据列的外部分区表
原生读取器: 与启用时的原生 Parquet/ORC 读取器不兼容
内置处理器: 不支持内置的 CsvStorageHandler/TsvStorageHandler
配置参数限制
odps.sql.unstructured.oss.metadata.columns.enabled=true: 启用/禁用元数据列功能,默认为false。odps.sql.type.system.odps2=true: 需要启用ODPS type2,默认为false。odps.sql.common.table.planner.ext.hive.bridge.v2=false: 禁用common tableodps.ext.parquet.native=false: 原生 Parquet 读取器兼容性(启用时元数据列不支持)。odps.ext.oss.orc.native=false: 原生 ORC 读取器兼容性(启用时元数据列不支持)。
支持格式限制
元数据列目前支持以下格式:
ORC 格式 (
org.apache.hadoop.hive.ql.io.orc.OrcSerde)Parquet 格式 (
org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe)JSON 格式 (
org.apache.hive.hcatalog.data.JsonSerDe)CSV 格式 (
org.apache.hadoop.hive.serde2.OpenCSVSerde)
使用示例
创建OSS外表
CREATE EXTERNAL TABLE [IF NOT EXISTS] <mc_oss_extable_name>
(
<col_name> <data_type>,
...
)
[comment <table_comment>]
[partitioned BY (<col_name> <data_type>, ...)]
[row format serde '<serde_class>'
[WITH serdeproperties (
['<property_name>'='<property_value>',...])
]
]
stored AS <file_format>
location '<oss_location>'
[USING '<resource_name>']
[tblproperties ('<tbproperty_name>'='<tbproperty_value>',...)];具体建表语法,请参见通过内置开源数据解析器创建外部表:OSS外部表。
基本元数据列查询
SET odps.sql.unstructured.oss.metadata.columns.enabled=true;
SET odps.sql.type.system.odps2=true;
-- 查询外部表中所有文件的元数据
SELECT
_mc_ext_file_path,
_mc_ext_file_simple_name,
_mc_ext_file_size,
_mc_ext_last_modified,
_mc_ext_etag,
_mc_ext_file_type,
_mc_ext_version_id,
_mc_ext_fs_state_name,
_mc_ext_owner,
_mc_ext_storage_class_name,
_mc_ext_storage_policy_name
FROM mc_ext_opencsv_web_sales;基于文件大小的过滤
SET odps.sql.unstructured.oss.metadata.columns.enabled=true;
SET odps.sql.type.system.odps2=true;
-- 仅处理大于 50 字节的文件
SELECT *, _mc_ext_file_simple_name, _mc_ext_file_size
FROM mc_ext_parquet_web_sales
WHERE _mc_ext_file_size > 50 limit 5;基于文件时间戳的过滤(JSON)
SET odps.sql.unstructured.oss.metadata.columns.enabled=true;
SET odps.sql.type.system.odps2=true;
-- 查找最近修改的文件
SELECT
_mc_ext_file_simple_name,
_mc_ext_file_size,
_mc_ext_last_modified,
_mc_ext_storage_class_name
FROM mc_ext_json_web_sales
ORDER BY _mc_ext_last_modified DESC
LIMIT 3;使用元数据聚合(ORC)
SET odps.sql.unstructured.oss.metadata.columns.enabled=true;
SET odps.sql.type.system.odps2=true;
-- 按文件类型聚合统计
SELECT
_mc_ext_file_type,
COUNT(*) as file_count,
SUM(_mc_ext_file_size) as total_size
FROM mc_ext_orc_web_sales
GROUP BY _mc_ext_file_type limit 5;使用元数据连接(opencsv&ORC)
SET odps.sql.unstructured.oss.metadata.columns.enabled=true;
SET odps.sql.type.system.odps2=true;
SET odps.stage.reducer.mem=12288;
SET odps.stage.joiner.mem=12288;
SET odps.stage.reducer.num=100;
SET odps.stage.joiner.num=500;
-- 连接两个外部表并访问其中一个的元数据
SELECT
p.ws_sold_date_sk,
p.ws_ext_sales_price,
p._mc_ext_file_simple_name,
p._mc_ext_file_size,
p._mc_ext_last_modified,
p._mc_ext_etag,
p._mc_ext_file_type,
p._mc_ext_version_id,
p._mc_ext_owner,
p._mc_ext_storage_class_name,
p._mc_ext_storage_policy_name,
p._mc_ext_fs_state_name,
p._mc_ext_file_path
FROM mc_ext_opencsv_web_sales j
JOIN mc_ext_orc_web_sales p
ON j.ws_sold_date_sk = p.ws_sold_date_sk
WHERE j.ws_sold_time_sk < 50
ORDER BY p.ws_sold_date_sk;错误处理示例
-- 尝试创建包含显式元数据列的表(将失败)
CREATE EXTERNAL TABLE invalid_table (
id INT,
_mc_ext_file_size STRING -- 这将导致错误,不允许创建reserved列
)
STORED AS json
LOCATION 'oss://bucket/path/';
-- 在禁用时查询元数据列(将失败)
SET odps.sql.unstructured.oss.metadata.columns.enabled = false;
SELECT _mc_ext_file_size FROM <external_table>; -- 找不到列