告别数据克隆:Vertex AI 直连 BigQuery 实践

让 Vertex AI 直连 BigQuery,用指针代替数据克隆,省下存储成本但需应对性能与快照挑战。
文章主张在 Vertex AI 训练流水线中放弃将特征从 BigQuery 或 Lakehouse 导出到新表的传统做法,改为通过 BigQuery DataFrames、SQL 或 `aiplatform.TabularDataset` 创建指向原始表的「指针」,让数据留在原地。这能消除 PB 级重复存储成本,并避免生产仓库与训练集出现两个版本的「真相」所带来的合规风险。但直连数仓并非银弹:查询性能受数仓并发槽位影响而变得非确定,训练中上游数据更新会引入快照偏差,Vertex AI SDK 版本波动也可能导致兼容错误。文章建议用严格分区、pin SDK 版本和幂等特征工程来应对,并明确该模式适合训练与批量推理,实时推理仍需 Feast 或 Vertex AI Feature Store。
告别数据克隆:Vertex AI 直连 BigQuery 实践
一个训练任务通过 CI,SRE 却突然在 Slack 上找你——流水线在一小时内吞掉了 400GB 存储。数据重复搬运正在悄悄吃掉你的云预算。
很多团队把 Lakehouse 当成只写一次的数据仓库:做完 ETL 和特征转换后,再把「特征」导出到一张新表或 GCS 桶里,供 Vertex AI 消费。这种习惯源于便利,但对预算和数据一致性来说是灾难。在医疗、金融等受监管行业,生产仓库和训练集里同时存在两个版本的「真相」,本身就是合规噩梦。
核心思路很直接:让数据待在原地。如果特征已经在 BigQuery 或 Delta Lake 中,就不要再移动它们。把 BigQuery 作为唯一事实来源,让 Vertex AI Pipelines 直接连接原始表或已处理表。

这套模式具体怎么跑通
目标是把你现有的数据仓库当成特征存储来用。实现方式是在 Kubeflow Pipelines(KFP)组件里调用 BigQuery DataFrames(BQF)或直接执行 SQL。
与其写一个 Spark 任务把特征导出成 CSV,不如写一个 KFP 组件执行 CREATE OR REPLACE TABLE,或者更好的做法——执行 CREATE VIEW,直接指向已经加工好的特征。
from kfp.dsl import component, Input, Output, Dataset
@component(base_image="python:3.9")
def train_model_op(project_id: str, query: str):
from google.cloud import bigquery
# 不物化数据,直接查询已有的 lakehouse 表
client = bigquery.Client(project=project_id)
job = client.query(query)
df = job.to_dataframe()
# 继续训练
# ...
真正的关键点在于从 to_dataframe() 转向 BigQuery ML(BQML)或 Vertex AI 的 BigqueryTabularDataset。通过 aiplatform.TabularDataset.create(display_name=..., bq_source=...),你创建的是一个「指针」,而不是数据副本。它相当于一个 URI,告诉 Vertex AI:需要训练时来这里读这张 BigQuery 表。
流水线运行时不再等待数据导出任务结束,只需要验证 schema 是否存在。如果使用 BigQuery Omni,这套方式甚至可以跨云工作。流水线触发、读取元数据、把特征映射进训练容器,成本集中在计算而不是存储上。
BigQuery DataFrames(BQF) 是 Google 提供的一个 Python 库,它把 pandas 和 scikit-learn 风格的 API 翻译成 BigQuery SQL,在服务端执行。底层依赖 BigQuery Storage Read API 和 Query API,因此可以处理远超单机内存的数据集。它的关键在于惰性执行:bqf.read_gbq() 返回的对象只是对 BigQuery 表的引用,直到调用 .to_pandas() 或 .execute() 才真正拉取数据。这意味着在 KFP 组件里可以用类似 pandas 的语法做特征工程,而实际计算发生在 BigQuery 内部,不需要把全量数据下载到容器里。
BigQuery ML(BQML) 则允许直接用 SQL 语句在 BigQuery 内训练和部署模型,例如 CREATE MODEL ... OPTIONS(model_type='logistic_reg') AS SELECT ...。它的价值在于把训练阶段也留在数仓内,避免导出特征。对于线性模型、 boosted trees、矩阵分解等常见算法,BQML 可以直接消费数仓中的表,并将模型注册为 BigQuery 资源。当与 Vertex AI 集成时,可以用 ML.PREDICT 做批量推理,或者通过 EXPORT MODEL 把模型导出到 GCS 供 Vertex AI Endpoint 使用。
没人愿意提的权衡代价
如果这是银弹,所有人早就这么干了。但这里有不少厂商喜欢轻描淡写带过的粗糙边缘。
性能表现是非确定性的。 如果特征工程逻辑涉及跨超大分区表的复杂 join,在训练循环中运行这条查询,等于跟数仓争夺并发槽位。如果 DBA 在你流水线物化特征时跑了一个重的 ANALYZE 命令,训练任务就会挂起。实际案例中,4 小时的训练窗口因为底层数仓资源争用被拉长到 12 小时。
数据快照问题。 不物化意味着你在用实时数据训练。这通常是我们想要的,但也很危险。如果上游流水线在训练运行途中更新了特征表,你就给模型引入了非确定性偏差。必须实现严格的分区——如果没有用 _PARTITIONTIME 或特定 run_id 作为 BigQuery 表的分区键,你实际上是在一个移动靶上训练。
Vertex AI SDK 版本波动。 使用 google-cloud-aiplatform>=1.30.0 时,它在 Dataset 资源里处理 bq_source 的方式与旧版本不同。有人花了整整一个下午调试 InvalidArgument 错误,最后发现是 SDK 中某个 BigQuery 元数据映射被弃用。务必在容器镜像的 requirements.txt 里 pin 住 SDK 版本,不要依赖 latest。
_PARTITIONTIME 与分区裁剪:BigQuery 中的分区表会为每个分区维护元数据,_PARTITIONTIME 是伪列,表示数据写入该分区的时间。用 WHERE _PARTITIONTIME = TIMESTAMP('2024-01-01') 可以只扫描一个分区,避免全表扫描。在「不复制」模式下,如果特征表按天分区,训练查询就必须显式限定分区范围,否则每次训练都会扫描全量历史数据,既慢又贵。更稳妥的做法是引入 run_id 或 snapshot_date 作为业务分区键,让每次训练固定读取一个不可变的分区快照,彻底消除训练中途上游写入带来的偏差。
并发槽位与资源争用:BigQuery 的按需计费模式有动态的并发查询限制,每个项目默认最多同时运行一定数量的查询(通常为 100 个槽位起步,具体随规模浮动)。当训练流水线在数仓高峰期发起大 join 查询时,可能进入排队状态。如果数仓同时有 ETL、BI 查询和训练任务,槽位竞争会直接拉长训练时间。缓解方式包括:为训练负载使用预留槽位(Reservations)、错峰调度、或者把重查询拆成多个小查询并利用 BigQuery 的物化视图缓存中间结果。
什么时候该用,什么时候不该用
当特征工程是幂等的、且数据仓库是主要事实来源时,适合采用「不复制」方案。如果 BigQuery 环境已经用 dbt 管理依赖,Vertex AI 流水线就应该只是一个编排器——在 dbt run 完成后触发模型构建。这样 lineage 保持干净,也不用重复造轮子。
需要极低延迟特征检索时不要用。如果模型服务要求亚毫秒级特征查找,查询 lakehouse 的开销——即便有 BigQuery 缓存——也还是太高。这种场景需要 Feast 或 Vertex AI Feature Store 这类专用特征存储。「不复制」模式针对的是训练和批量推理,不是实时推理。
数据科学家还在探索阶段时也不要急着用。如果他们频繁删列、重命名字段、把 schema 改得面目全非,你不会希望他们直接动生产 BigQuery 表。让他们在沙盒环境里随便复制,等特征逻辑固化成生产 dbt 模型后再迁移到「不复制」流水线。
Feast 是一个开源的特征存储(Feature Store),核心能力是统一离线训练和在线推理的特征定义,并保证两者之间的一致性。离线侧它从 BigQuery、Snowflake 等数仓读取历史特征用于训练;在线侧它把最新特征值同步到 Redis、Bigtable 等低延迟存储,供模型服务在毫秒级内查找。Vertex AI Feature Store 是 Google 的托管版本,与 Vertex AI 训练和预测服务深度集成,支持按需特征服务和在线服务。两者解决的是同一个问题:当模型需要实时特征时,不可能每次都去查 lakehouse。因此「不复制」模式与特征存储并不是互斥的——常见架构是训练阶段直连 BigQuery,在线推理阶段用 Feast 或 Vertex AI Feature Store 提供低延迟特征,而特征定义和回填逻辑保持同源。
守住数据,守住预算
数据重复是加在工程团队和云账单上的双重税。把 BigQuery 或 Lakehouse 视为训练流水线的 API,而不是存储柜,就能重新掌控 lineage。这会倒逼你在 schema 和分区上保持纪律,对长期维护来说是好事。
把流水线发出去,让数据留在原地,别再为同一批字节付两次钱。
相关推荐

黑人觅食者Alexis Nelson:从觅食到出书,快乐零食别死
黑人觅食者Alexis Nikole Nelson做客Talks at Google,分享从童年跟随母亲认识植物到出版食谱书《Happy Snacking, Don't Die!》的旅程,揭示美国觅食兴衰背后的种族与土地历史。

如何有效呈现你的想法:三种揭示式演讲方法
揭示式演讲是传递想法最直接的方式。本文解析奇妙漫步、动态演示和梦想图景三种演讲类型,结合David Gayo、Michael Pritchard、Sylvia Earle等经典案例,教你如何有效呈现想法。

Modern Web Guidance:让AI写原生声明式对话弹窗
Google Modern Web Guidance 让 AI 编程助手在生成代码时优先使用浏览器原生声明式 dialog 能力,替代重复的 JavaScript 事件监听,实测可显著减少样板代码。