设计 DWaaS:与 Databricks、Snowflake、Fabric 的架构差距
明确结论
现有的 ETL、元数据和数据质量平台可以作为 DWaaS 的基础,但只有 pipeline orchestration 还不能称为 DWaaS。以下五项能力缺一不可:
- 持久、可共享的数据存储层;
- 面向租户的 SQL 查询服务;
- 计算资源生命周期和工作负载管理;
- 覆盖查询、存储与元数据的安全隔离;
- 可观测、可计量、可恢复的控制平面。
在生产环境中,compute/storage separation、弹性扩缩容和高可用通常也必不可少,但第一版不需要达到 Snowflake 的成熟度。流处理、语义层、自动优化、跨区域复制和精细化计费,可以在后续阶段逐步加入。
较稳妥的演进方式是保留现有 ETL 和 metadata layer,将其升级为统一控制平面。同时尽早引入 SQL query engine,先提供基于共享存储的查询服务,再逐步完善租户隔离、弹性计算、治理和计量。
什么才算 DWaaS
DWaaS 的判断标准不是平台是否拥有数据仓库组件,而是能否把数据仓库能力作为服务交付。用户提交 SQL 或 API 请求后,平台应完成身份验证、资源分配、查询执行、故障恢复、权限检查和用量记录,无需用户直接管理集群。
1. 统一且持久的数据存储
数据必须独立于单个计算节点存在。常见方案包括:
- 对象存储加开放表格式,例如 S3-compatible storage + Apache Iceberg;
- 云对象存储加平台自有表格式;
- 分布式数据库或分析型数据库自身的持久化存储。
第一版可以不做完全无状态的计算层,但不能只把业务数据保存在临时计算节点的本地磁盘上。否则,集群扩缩容、故障恢复以及多个计算集群共享数据都会变得困难。
2. 可持续提供的 SQL 查询服务
DWaaS 至少需要一个稳定的查询入口,涵盖:
- SQL endpoint、JDBC、ODBC 或 REST API;
- 查询解析、规划、执行和结果返回;
- 查询取消、超时、重试和状态查询;
- 并发控制与资源配额;
- 统一的 catalog、schema 和 table 命名空间。
语义层不是前置条件。初期可以直接提供表、视图和 SQL,指标定义、业务模型及 BI semantic model 后续再做。
3. Compute abstraction
用户看到的应该是 warehouse、endpoint、pool 或 cluster 这类逻辑资源,而不是 Kubernetes Pod、worker IP 或底层虚拟机。
一个计算抽象至少需要描述:
warehouse:
name: analytics-medium
tenant_id: tenant-a
engine: trino
size: medium
min_workers: 0
max_workers: 20
auto_suspend_seconds: 300
concurrency_limit: 30
queue_timeout_seconds: 120
catalog_policy: analytics-readonly
控制平面要把这份逻辑配置转换成实际资源,并负责:
- 创建或唤醒计算资源;
- 把查询路由到正确的 coordinator;
- 根据队列长度、CPU、内存等指标扩缩容;
- 在资源空闲后将其暂停或销毁;
- 记录查询消耗;
- 向用户隐藏底层节点变化。
早期没有必要实现任意规模的瞬时扩容。先支持固定规格、手动扩缩容和空闲暂停,通常足以验证产品模型。
4. 多租户安全边界
仅在 API 中携带 tenant_id 并不能形成可靠隔离。租户上下文必须贯穿整个请求链:
Identity
→ Tenant context
→ Warehouse routing
→ Catalog authorization
→ Object storage credentials
→ Query audit and usage metering
至少要覆盖:
- 身份认证和 tenant membership;
- warehouse、catalog、schema、table 级授权;
- 凭证隔离;
- 查询与数据访问审计;
- CPU、内存、并发数和扫描量配额;
- 元数据过滤,避免租户看到无权访问的对象;
- 网络隔离或运行时隔离。
早期可以让多个租户共享计算集群,但资源配额和数据访问隔离必须到位。对于安全要求较高的客户,可以提供独占 warehouse、独立 namespace、独立云账号或独立集群。
5. 控制平面与可观测性
控制平面需要管理:
- 租户和身份;
- warehouse 定义与生命周期;
- catalog 和数据对象;
- 查询状态;
- 策略和权限;
- 作业与调度;
- 用量和审计事件。
重要状态需要持久化,API 操作也应尽量保持幂等。例如,创建 warehouse 时可以使用幂等键,避免客户端重试产生重复资源:
POST /v1/warehouses
Idempotency-Key: 7aa1c37e-8b44-4f94-b94e-f4ef519aac86
Content-Type: application/json
{
"name": "analytics-medium",
"tenantId": "tenant-a",
"size": "medium",
"autoSuspendSeconds": 300
}
可观测性也需要从一开始建设。缺少 query ID、trace、资源指标和审计日志,就很难排查慢查询、资源争抢、错误计费或越权访问。
Snowflake 一类平台如何抽象计算资源
Snowflake 的内部实现包含专有设计。外部只能根据公开的架构说明做高层概括,不能把它简单等同于某个开源组件。
从整体上看,这类平台通常分为三层:
Cloud services / control plane
├─ Authentication and authorization
├─ Metadata and transaction coordination
├─ Query parsing, optimization and routing
└─ Resource lifecycle and metering
Independent compute
├─ Warehouse A: BI workload
├─ Warehouse B: ETL workload
└─ Warehouse C: data science workload
Shared storage
├─ Table data
├─ Metadata
└─ Persistent intermediate state
warehouse abstraction 是控制平面提供的逻辑计算边界。每个 warehouse 有自己的执行资源、并发状态和缓存,可以独立扩缩或暂停。数据保存在共享持久层,因此暂停计算不会造成数据丢失。
compute/storage separation 并不意味着计算节点上完全没有本地状态。执行节点通常仍会使用内存、本地磁盘缓存、shuffle 数据和临时结果。真正需要分离的是数据的持久所有权。即使计算资源被销毁,权威数据仍然存在,其他计算资源也能重新访问。
自建平台可以参考这种模型,但不需要复制具体实现。第一阶段可以将一个 warehouse 映射为:
- 一个独立 Trino cluster;
- 一个 Kubernetes namespace 中的 engine deployment;
- 一个共享集群中的 resource group;
- 一个 ClickHouse、StarRocks 或 Doris compute group。
不同方案的隔离强度和成本也不同。将每个 warehouse 映射为独立集群,隔离边界清楚,但启动速度较慢。共享集群成本更低,不过对 workload management 的要求更高。
是否应该尽早引入 query engine
应该尽早引入,但不必立即把所有 ETL 都交给它。
ETL-first 是合理的起点,特别是在数据接入、清洗和质量控制还不稳定的时候。但在查询引擎出现之前,平台只能证明自己能够生产数据,无法证明它可以持续交付仓库服务。查询引擎还会提前暴露一些纯 pipeline 模式难以发现的问题:
- 多用户并发;
- 小查询和大查询争抢资源;
- catalog 一致性;
- schema evolution;
- 权限下推;
- 大规模 scan、join、shuffle 和 spill;
- 查询取消与超时;
- 成本归属。
建议保留两条执行路径,不必强行统一:
Batch path:
API → Orchestrator → Spark/Flink/dbt → Shared tables
Interactive path:
JDBC/REST → Query gateway → Trino/other engine → Shared tables
两条路径共用 catalog、权限、表格式和审计体系。这样既能保留现有的 pipeline 投资,也能尽早验证 DWaaS 的核心假设。
如果主要场景是交互式联邦查询,Trino 是常见选项。如果希望由单一引擎同时承担高并发分析和数据写入,也可以评估 ClickHouse、Apache Doris 或 StarRocks。选型时不能只比较 SQL 覆盖率,还要测试并发能力、写入语义、事务能力、资源隔离和运维复杂度。
转型过程中最常见的扩展瓶颈
元数据控制面
pipeline 系统访问元数据的并发通常不高。查询平台则可能在每次解析、优化和授权时读取大量表、分区及统计信息。常见问题有:
- catalog 单点;
- 高频
list partitions; - 大表统计信息读取缓慢;
- schema cache 失效风暴;
- 元数据数据库锁竞争。
catalog 服务应与业务编排数据库解耦,并支持缓存、分页、版本号以及事件驱动的 cache invalidation。不要让每个查询都直接扫描 pipeline metadata 表。
并发和资源争抢
最先遇到的瓶颈未必是总数据量,往往是查询并发。一个失控的大型 join 就可能耗尽集群内存,拖慢所有租户。
以下能力需要尽早建立:
- admission control;
- resource group;
- per-tenant quota;
- 查询优先级;
- memory limit;
- spill 策略;
- 超时和取消;
- ETL 与 BI 工作负载隔离。
对象存储请求与小文件
共享对象存储容量很大,但文件数量、请求次数和读取放大会直接影响延迟与成本。频繁的小批次写入容易制造大量小文件,拖慢 planning 和 scan。
compaction、snapshot expiration、orphan file cleanup 和统计信息收集应作为平台维护任务统一执行,不能全部交给用户处理。
Shuffle、网络和数据倾斜
大规模聚合与 join 会产生大量网络传输。热点 key、错误的 join order 或不准确的统计信息,都可能让少数 worker 过载。
除扩容以外,还需要:
- 成本优化器和统计信息;
- partition pruning;
- dynamic filtering;
- broadcast join 限制;
- 数据倾斜检测;
- local spill 和 remote spill 规划。
多租户隔离
共享集群中的 noisy neighbor 会同时影响性能和成本。只根据 CPU 扩缩容,无法解决内存、网络、对象存储带宽及 coordinator 压力。
可以把隔离分为三个级别:
| 隔离方式 | 成本 | 隔离强度 | 适用场景 |
|---|---|---|---|
| 共享集群 + resource group | 低 | 中低 | 开发环境、小租户 |
| 独立 warehouse 集群 | 中 | 高 | 生产分析、重要客户 |
| 独立账号或基础设施 | 高 | 很高 | 强合规、监管场景 |
计量准确性
usage-based billing 不能只统计集群运行时间。在共享集群中,还需要确定 CPU、内存、扫描字节、对象存储请求和空闲资源的分摊方式。
第一阶段可以先建立一套容易解释的内部计量:
{
"tenantId": "tenant-a",
"warehouseId": "wh-42",
"queryId": "q-20260901-001",
"startedAt": "2026-09-01T02:10:00Z",
"durationMs": 18420,
"cpuMs": 73110,
"peakMemoryBytes": 2147483648,
"processedBytes": 68719476736,
"spilledBytes": 0,
"status": "SUCCEEDED"
}
先确保事件不会重复,而且可以追踪和重放,再设计价格模型。否则计费规则发生变化后,很难根据历史数据重新计算。
可组合的开源组件
以下组件是架构候选,不是一套可以直接拼成 Snowflake 的固定清单。不同组件之间的权限、事务和元数据语义,都需要经过实际集成验证。
Query engine
- Trino:适合交互式 SQL、联邦查询和多 catalog 场景;
- Apache Spark:适合大规模批处理、复杂转换和机器学习;
- Apache Flink:适合流处理、持续计算和低延迟状态计算;
- ClickHouse:适合低延迟 OLAP 和高并发聚合;
- Apache Doris / StarRocks:适合实时分析、物化视图和面向 BI 的服务。
第一阶段不建议同时引入多个交互式 SQL 引擎。应先确定一个主查询引擎,再由 Spark 或 Flink 处理它不擅长的任务。
Table format 和 catalog
- Apache Iceberg:适合开放湖仓表、snapshot、schema evolution 和多引擎访问;
- Delta Lake:与 Spark 生态结合紧密;
- Apache Hudi:适合增量写入、CDC 和数据湖更新场景;
- Project Nessie:提供面向数据湖 catalog 的版本管理能力;
- Hive Metastore:兼容范围广,但大规模部署时需要谨慎处理扩展性和缓存;
- OpenMetadata / DataHub:适合数据发现、技术元数据和 lineage 展示。
table catalog 和 metadata discovery catalog 是两个不同的概念。前者参与查询正确性与表事务,后者主要用于搜索、治理和数据资产管理。搜索型 metadata 产品不能替代事务性 catalog。
Governance 和授权
- Apache Ranger:用于集中策略管理,并可与部分引擎集成;
- Open Policy Agent:适合通用策略判断和 API authorization;
- OpenLineage + Marquez:适合采集及展示作业 lineage;
- Keycloak:可用于 OIDC、身份认证和组织映射。
权限必须在实际读取数据的位置执行。只在 UI 或 API gateway 中隐藏表名,无法阻止用户通过 JDBC 或其他引擎读取数据。
Orchestration、质量和可观测性
- Apache Airflow / Dagster:用于批处理编排;
- Argo Workflows:Kubernetes-native workflow;
- dbt Core:用于 SQL transformation 和模型依赖管理;
- Great Expectations / Soda Core:用于数据质量检查;
- Prometheus + Grafana:用于基础指标;
- OpenTelemetry:用于跨 API、调度器和执行引擎的 trace;
- OpenSearch:用于检索查询日志和审计事件。
如果现有 orchestration 和 quality 模块运行稳定,没有必要为了平台化而全部替换。更需要做的是让这些模块使用统一的事件、身份和资源标识,并产出统一的 lineage。
推荐的分阶段实施顺序
第一阶段:建立共享数据平面
确定对象存储和主表格式,统一 catalog,并让现有 ETL 产出的表可以被其他引擎读取。先处理 schema evolution、原子提交、小文件治理和失败恢复。
第二阶段:引入查询服务
部署一个主 SQL engine,提供 JDBC、ODBC 或 REST endpoint,并加入查询状态、取消、超时、结果分页和审计日志。完成这些工作后,平台便具备了最小的 DWaaS 形态。
第三阶段:形成 warehouse abstraction
在查询引擎之上增加 warehouse API、资源规格、路由、队列、自动暂停和租户配额。初期可以只提供 small、medium、large 等少量离散规格,这比一开始就支持任意资源组合更容易运营。
第四阶段:强化隔离和治理
加入 RBAC、行列级策略、凭证隔离、资源组、独占 warehouse 和端到端 lineage。对于高敏感租户,则提供更强的基础设施边界。
第五阶段:弹性和计费
积累真实 workload 后,再调整 autoscaling。扩缩容不能只参考 CPU,还要考虑:
- queued queries;
- running queries;
- memory pressure;
- split backlog;
- coordinator load;
- cache warm-up cost;
- cluster startup latency。
最后,再把已经验证的 usage event 转换成账单。计量系统和支付系统应彼此独立。前者记录事实,后者应用价格和折扣规则。
需要避免的架构误区
第一,不要把 Kubernetes 当作 compute abstraction。Kubernetes 负责资源编排,但 warehouse 的状态、路由、策略、暂停和计量仍然要由平台控制平面实现。
第二,不要过早承诺 “serverless”。如果查询冷启动需要数分钟,扩缩容会中断查询,或者空闲资源无法可靠回收,那么 managed warehouse 是更准确的描述。
第三,不要让每个引擎各自维护权限和表定义。多个 catalog、身份模型和策略来源很快就会出现不一致。控制平面应保留统一的身份和策略来源,同时明确哪些策略可以下推到执行引擎。
第四,不要先设计复杂计费,再处理资源隔离。如果没有稳定的 query attribution 和资源边界,再精细的价格规则也无法保证公平。
最终目标不是复制 Databricks、Snowflake 或 Fabric 的所有功能,而是先打通一条完整链路:租户创建 warehouse 并提交 SQL,平台安全地分配资源、访问共享数据、返回结果,同时记录完整用量。这条链路稳定后,其他能力都可以围绕它逐步完善。
备注:内容仅供参考。
