Metadata Column利用OSS元信息支持AI数据预处理

更新时间:
复制 MD 格式

背景介绍

MaxCompute 支持外部表的元数据列(Metadata Column),允许在 SQL 查询中直接访问底层存储(特别是OSS)的文件级元数据,如文件大小、最后修改时间、文件路径等。用户可基于这些属性对数据进行过滤、排序或分析,扩展外部表的使用场景。

在 AI 数据预处理场景中,OSS 上的原始数据通常由业务系统自动生成,后续用于 AI 训练或调优。并非所有数据都需要参与训练,需按特定条件筛选。而筛选条件往往不是数据列的值,而是文件本身的属性,例如文件大小、生成时间或命名规范。元数据列功能允许按文件元信息(而非数据列)过滤数据,从而快速获取 AI 训练所需的数据集。

实现的核心是通过以下几方面增强现有架构:

  • 添加 11 种预定义元数据列供查询。

  • 增强 Hive 输入格式包装器,检测并处理元数据列。

  • 集成 JindoOSS 文件系统以获取文件元数据。

  • 优化 SQL 编译器,验证和解析元数据列。

支持的元数据列

  • 元数据列不属于数据列,默认不会出现,也无法通过 SELECT * 或其他通配符查询。需通过 _*** 形式查询。

  • 元数据列后续会持续增加。

列名

类型

说明

默认值

_mc_ext_file_path

VARCHAR(2048)

文件的完整 OSS 路径

默认为OSS路径:oss://bucket/path/test.json

_mc_ext_file_simple_name

VARCHAR(256)

不含路径的文件基本名称

OSS上的文件名,例如:test.json

_mc_ext_file_size

BIGINT

文件大小(字节)

无

_mc_ext_last_modified

TIMESTAMP

最后修改时间

无

_mc_ext_etag

VARCHAR(64)

OSS ETag 签名信息

无

_mc_ext_file_type

VARCHAR(32)

文件类型。取值为:

  • FILE:普通文件

  • DIRECTORY:目录

  • SYMLINK:符号链接

  • MOUNT_POINT:挂载点

  • UNKNOWN:未知或无法识别的文件类型

FILE

_mc_ext_version_id

VARCHAR(32)

文件版本信息(可能为 NULL)

NULL

_mc_ext_owner

VARCHAR(32)

文件所有者信息

无

_mc_ext_storage_class_name

VARCHAR(32)

存储类别。取值为:

  • Standard:标准存储

  • IA:低频访问存储

  • Archive:归档存储

  • ColdArchive:冷归档存储

  • DeepColdArchive:深度冷归档存储

  • Unknown:未知或无法识别的存储类别

Standard

_mc_ext_storage_policy_name

VARCHAR(32)

存储策略。取值为:

  • UNSPECIFIED:未指定存储策略

  • HOT:热存储策略

  • WARM:温存储策略

  • COLD:冷存储策略

  • ALL_SSD:全 SSD 存储策略

  • ONE_SSD:单个副本使用 SSD 的存储策略

  • LAZY_PERSIST:内存写入、延迟持久化策略

  • PROVIDED:Provided 存储策略;当前 SDK 也将未匹配到明确分支的策略值映射为此字符串

  • CLOUD_STD:云端标准存储策略

  • CLOUD_IA:云端低频访问存储策略

  • CLOUD_AR:云端归档存储策略

  • CLOUD_COLD_AR:云端冷归档存储策略

UNSPECIFIED

_mc_ext_fs_state_name

VARCHAR(32)

文件系统状态。取值为:

  • CREATING:创建中

  • FINALIZED:已完成创建

  • CLOUD_STD:云端标准存储状态

  • CLOUD_IA:云端低频访问存储状态

  • CLOUD_AR:云端归档存储状态

  • CLOUD_COLD_AR:云端冷归档存储状态

  • CLOUD_DEEP_COLD_AR:云端深度冷归档存储状态

  • UNKNOWN:未知或无法识别的文件系统状态

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 table

  • odps.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外部表。

基本元数据列查询

准备数据

CREATE EXTERNAL TABLE mc_ext_opencsv_web_sales
(
    ws_sold_date_sk BIGINT, 
    ws_sold_time_sk BIGINT, 
    ws_ship_date_sk BIGINT, 
    ws_item_sk BIGINT NOT NULL, 
    ws_bill_customer_sk BIGINT, 
    ws_bill_cdemo_sk BIGINT, 
    ws_bill_hdemo_sk BIGINT, 
    ws_bill_addr_sk BIGINT, 
    ws_ship_customer_sk BIGINT, 
    ws_ship_cdemo_sk BIGINT, 
    ws_ship_hdemo_sk BIGINT, 
    ws_ship_addr_sk BIGINT, 
    ws_web_page_sk BIGINT, 
    ws_web_site_sk BIGINT, 
    ws_ship_mode_sk BIGINT, 
    ws_warehouse_sk BIGINT, 
    ws_promo_sk BIGINT, 
    ws_order_number BIGINT NOT NULL, 
    ws_quantity BIGINT, 
    ws_wholesale_cost DECIMAL(7,2), 
    ws_list_price DECIMAL(7,2), 
    ws_sales_price DECIMAL(7,2), 
    ws_ext_discount_amt DECIMAL(7,2), 
    ws_ext_sales_price DECIMAL(7,2), 
    ws_ext_wholesale_cost DECIMAL(7,2), 
    ws_ext_list_price DECIMAL(7,2), 
    ws_ext_tax DECIMAL(7,2), 
    ws_coupon_amt DECIMAL(7,2), 
    ws_ext_ship_cost DECIMAL(7,2), 
    ws_net_paid DECIMAL(7,2), 
    ws_net_paid_inc_tax DECIMAL(7,2), 
    ws_net_paid_inc_ship DECIMAL(7,2), 
    ws_net_paid_inc_ship_tax DECIMAL(7,2), 
    ws_net_profit DECIMAL(7,2)
)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.OpenCSVSerde'
WITH serdeproperties (
  'odps.properties.rolearn'='acs:ram::<uid>:role/aliyunodpsdefaultrole'
)
STORED AS TEXTFILE 
LOCATION 'oss://oss-<region>-internal.aliyuncs.com/<oss_path>/'
;

SET odps.namespace.schema=true;
INSERT OVERWRITE TABLE mc_ext_opencsv_web_sales SELECT * FROM bigdata_public_dataset.tpcds_100g.web_sales;

SELECT COUNT(*) FROM mc_ext_opencsv_web_sales;
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;

基于文件大小的过滤

准备数据

-- 建表语句示例
CREATE EXTERNAL TABLE IF NOT EXISTS mc_ext_parquet_web_sales(
    ws_sold_date_sk BIGINT, 
    ws_sold_time_sk BIGINT, 
    ws_ship_date_sk BIGINT, 
    ws_item_sk BIGINT NOT NULL, 
    ws_bill_customer_sk BIGINT, 
    ws_bill_cdemo_sk BIGINT, 
    ws_bill_hdemo_sk BIGINT, 
    ws_bill_addr_sk BIGINT, 
    ws_ship_customer_sk BIGINT, 
    ws_ship_cdemo_sk BIGINT, 
    ws_ship_hdemo_sk BIGINT, 
    ws_ship_addr_sk BIGINT, 
    ws_web_page_sk BIGINT, 
    ws_web_site_sk BIGINT, 
    ws_ship_mode_sk BIGINT, 
    ws_warehouse_sk BIGINT, 
    ws_promo_sk BIGINT, 
    ws_order_number BIGINT NOT NULL, 
    ws_quantity BIGINT, 
    ws_wholesale_cost DECIMAL(7,2), 
    ws_list_price DECIMAL(7,2), 
    ws_sales_price DECIMAL(7,2), 
    ws_ext_discount_amt DECIMAL(7,2), 
    ws_ext_sales_price DECIMAL(7,2), 
    ws_ext_wholesale_cost DECIMAL(7,2), 
    ws_ext_list_price DECIMAL(7,2), 
    ws_ext_tax DECIMAL(7,2), 
    ws_coupon_amt DECIMAL(7,2), 
    ws_ext_ship_cost DECIMAL(7,2), 
    ws_net_paid DECIMAL(7,2), 
    ws_net_paid_inc_tax DECIMAL(7,2), 
    ws_net_paid_inc_ship DECIMAL(7,2), 
    ws_net_paid_inc_ship_tax DECIMAL(7,2), 
    ws_net_profit DECIMAL(7,2)) 
    STORED AS parquet 
    LOCATION 'oss://oss-<region>-internal.aliyuncs.com/<oss_path>/';
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)

准备数据

-- 建表语句示例
CREATE EXTERNAL TABLE IF NOT EXISTS mc_ext_json_web_sales(
    ws_sold_date_sk BIGINT, 
    ws_sold_time_sk BIGINT, 
    ws_ship_date_sk BIGINT, 
    ws_item_sk BIGINT NOT NULL, 
    ws_bill_customer_sk BIGINT, 
    ws_bill_cdemo_sk BIGINT, 
    ws_bill_hdemo_sk BIGINT, 
    ws_bill_addr_sk BIGINT, 
    ws_ship_customer_sk BIGINT, 
    ws_ship_cdemo_sk BIGINT, 
    ws_ship_hdemo_sk BIGINT, 
    ws_ship_addr_sk BIGINT, 
    ws_web_page_sk BIGINT, 
    ws_web_site_sk BIGINT, 
    ws_ship_mode_sk BIGINT,
    ws_warehouse_sk BIGINT, 
    ws_promo_sk BIGINT, 
    ws_order_number BIGINT NOT NULL, 
    ws_quantity BIGINT, 
    ws_wholesale_cost DECIMAL(7,2), 
    ws_list_price DECIMAL(7,2), 
    ws_sales_price DECIMAL(7,2), 
    ws_ext_discount_amt DECIMAL(7,2), 
    ws_ext_sales_price DECIMAL(7,2), 
    ws_ext_wholesale_cost DECIMAL(7,2), 
    ws_ext_list_price DECIMAL(7,2), 
    ws_ext_tax DECIMAL(7,2), 
    ws_coupon_amt DECIMAL(7,2), 
    ws_ext_ship_cost DECIMAL(7,2), 
    ws_net_paid DECIMAL(7,2), 
    ws_net_paid_inc_tax DECIMAL(7,2), 
    ws_net_paid_inc_ship DECIMAL(7,2), 
    ws_net_paid_inc_ship_tax DECIMAL(7,2), 
    ws_net_profit DECIMAL(7,2)) 
    STORED AS json 
    LOCATION 'oss://oss-<region>-internal.aliyuncs.com/<oss_path>/';
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)

准备数据

-- 建表语句示例
CREATE EXTERNAL TABLE IF NOT EXISTS mc_ext_orc_web_sales(
    ws_sold_date_sk BIGINT, 
    ws_sold_time_sk BIGINT, 
    ws_ship_date_sk BIGINT, 
    ws_item_sk BIGINT NOT NULL, 
    ws_bill_customer_sk BIGINT, 
    ws_bill_cdemo_sk BIGINT, 
    ws_bill_hdemo_sk BIGINT, 
    ws_bill_addr_sk BIGINT, 
    ws_ship_customer_sk BIGINT, 
    ws_ship_cdemo_sk BIGINT, 
    ws_ship_hdemo_sk BIGINT, 
    ws_ship_addr_sk BIGINT, 
    ws_web_page_sk BIGINT, 
    ws_web_site_sk BIGINT, 
    ws_ship_mode_sk BIGINT, 
    ws_warehouse_sk BIGINT, 
    ws_promo_sk BIGINT, 
    ws_order_number BIGINT NOT NULL, 
    ws_quantity BIGINT, 
    ws_wholesale_cost DECIMAL(7,2), 
    ws_list_price DECIMAL(7,2), 
    ws_sales_price DECIMAL(7,2), 
    ws_ext_discount_amt DECIMAL(7,2), 
    ws_ext_sales_price DECIMAL(7,2), 
    ws_ext_wholesale_cost DECIMAL(7,2), 
    ws_ext_list_price DECIMAL(7,2), 
    ws_ext_tax DECIMAL(7,2), 
    ws_coupon_amt DECIMAL(7,2), 
    ws_ext_ship_cost DECIMAL(7,2), 
    ws_net_paid DECIMAL(7,2), 
    ws_net_paid_inc_tax DECIMAL(7,2), 
    ws_net_paid_inc_ship DECIMAL(7,2), 
    ws_net_paid_inc_ship_tax DECIMAL(7,2), 
    ws_net_profit DECIMAL(7,2)) 
    STORED AS orc 
    LOCATION 'oss://oss-<region>-internal.aliyuncs.com/<oss_path>/' 
    TBLPROPERTIES ('mcfed.orc.compress'='SNAPPY');
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>;  -- 找不到列