数据仓库慢查询怎么办?Redshift、BigQuery、Snowflake性能调优实战

去年一个客户,数据仓库换成了Redshift,觉得“云数仓肯定比自建快”。结果跑了两个月,业务方反馈报表越跑越慢,季度汇总从10分钟变成40分钟。他们又准备扩容。
我说:“先别扩,我看一眼查询。”
慢查询日志里有一条:扫描了全表,过滤条件在where里,但没有用排序键。表按日期排序,但查询按用户ID过滤。每次都要扫全表——数据量大了当然慢。
加了排序键后,同样的查询从40分钟缩到6分钟。没扩一个节点。
这是数仓优化最常见的误区:觉得慢就是资源不够,其实往往是用法不对。
今天聊聊Redshift、BigQuery、Snowflake的性能调优。不是那种“数仓很快”的入门课,而是帮你理清楚:不同数仓的优化重点在哪里?什么情况下扩仓是最后手段?
01 数仓优化三板斧:存储、查询、资源
数仓慢,逃不出这三个方面的问题。
| 维度 | 核心问题 | 优化方向 |
|---|---|---|
| 存储 | 数据怎么放 | 排序键、分区、压缩、分布风格 |
| 查询 | SQL怎么写 | 过滤下推、聚合前置、Join顺序 |
| 资源 | 并发怎么管 | 队列、超时、资源隔离 |
那家客户的慢查询,存储层问题。表按日期排序,查询按用户ID过滤,全表扫描。加了一个按用户ID的排序键后,查询只扫相关数据块。
02 存储优化:数据怎么放
数仓和OLTP数据库不一样,没有传统意义上的索引。性能靠数据组织。
Redshift:排序键和分布风格
排序键:决定数据在磁盘上的物理顺序。查询经常用作过滤条件的列,设为排序键。一张表最多设一个排序键(复合排序键也是按顺序存储)。
分布风格:决定数据在节点间的分布。键分布(按某列hash)适合该列经常被Join;全分布(所有节点全量复制)适合维度表;自动分布适合小表。
那家客户的表,分布风格是自动,数据随机分布。查询Join时大量数据跨节点传输。改成按用户ID键分布后,Join时数据已在本地,传输量大降。
BigQuery:分区和聚簇
分区:按时间分区,查询只扫描相关分区。必须指定分区列。
聚簇:按某几列自动组织数据存储顺序。适合高基数列(如用户ID)。
Snowflake:自动优化为主
Snowflake自动管理微分区和统计信息,用户干预少。主要优化查询写法。
03 查询优化:SQL怎么写
存储层优化完了,看查询怎么写。
避免全表扫描:能用分区键过滤的,一定用。能用排序键过滤的,一定用。
过滤下推:把过滤条件尽早执行,减少数据传输量。Redshift的WHERE条件会在扫描时应用;BigQuery的WHERE会下推到存储层。
聚合前置:子查询里先聚合,再Join。减少Join的数据量。
Join顺序:小表在左,大表在右(取决于优化器)。但大多数现代数仓优化器会自动重排,不用过分纠结。关键是避免笛卡尔积。
避免SELECT *:只取需要的列。列存数据库读取列越多,IO越大。
那家客户有一条查询,先Join再聚合,扫描了全表。改成先聚合再Join,数据量从10亿行降到几万行,查询时间从15分钟降到2分钟。
04 资源管理:并发不是越高越好
数仓是共享资源池。并发太高,大家都慢。
Redshift:查询队列(WLM)
按查询类型分队列:短查询、长查询、ETL分开
设并发上限:一个队列最多5个并发,超过排队
设超时:超过时间自动取消
BigQuery:槽位(Slot)
按项目分配槽位
按需模式:公共槽池共享
固定模式:购买专用槽位
Snowflake:虚拟仓库
一个仓库一套资源。不同业务用不同仓库,资源隔离
按查询负载决定仓库大小(Small到XXLarge)
多仓库并发,互不影响
那家客户把所有查询放一个队列,长查询占满资源,短查询排队等。后来把BI查询和ETL分开队列,BI查询不再被ETL阻塞。
05 物化视图:预聚合加速
对于固定模式的聚合查询(如每日报表),可以建物化视图。
Redshift:支持物化视图,自动增量刷新
BigQuery:支持物化视图,自动查询重写
Snowflake:支持物化视图,需要手动刷新
那家客户有张日报表,每天跑一次,全量聚合。建了物化视图按天预聚合,查询直接读物化视图,从10分钟降到几秒。
06 一个真实案例:从40分钟到6分钟
一个客户,季度汇总报表从10分钟涨到40分钟。我们做了几件事:
加排序键:表原按日期排序,但查询按用户ID过滤。在用户ID上建排序键(复合排序键:用户ID在前,日期在后)。扫描数据量大降。
改分布风格:从随机分布改成按用户ID键分布。Join时数据本地化。
改查询写法:子查询先聚合,再Join主表。
拆分长查询:一个巨复杂的SQL拆成3个临时表,分段执行。
不扩一个节点,查询从40分钟缩到6分钟。运维负责人说:“以前以为数仓慢就是资源不够,现在知道,先查数据怎么放的,再查SQL怎么写的,最后才考虑扩仓。”
写在最后
数仓慢,第一反应不是“加钱扩仓”。先看存储:数据怎么放的?排序键对吗?分布风格对吗?再看SQL:过滤下推了吗?聚合前置了吗?最后才看资源:队列堵了吗?并发超了吗?
那家客户的运维负责人后来总结了一个口诀:“存储先看排序键,过滤下推少扫描;聚合前置减数据,队列隔离防堵塞;扩仓是最后手段,别当第一板斧。”
你的数仓慢,是资源不够,还是用法不对?