4. 高频SQL与查询优化面试题
在ClickHouse金融数据岗位面试中,SQL与查询优化几乎是必考环节。面试官并不满足于你会写SQL,而是想确认你是否理解ClickHouse的执行模型:数据如何被裁剪、谓词何时下推、JOIN在分布式场景下如何选择策略。本章围绕五个高频关键词展开,帮助你建立可复述、可追问、可落地的回答框架。
一、核心原理:ClickHouse为什么快
ClickHouse的快来自列式存储、向量化执行、稀疏索引和分区裁剪的组合。面试时不要只背“列存快”,而要讲清楚一条查询从SQL到结果经历了哪些裁剪:分区裁剪先排除整块数据,主键索引再排除granule,PREWHERE在读取阶段提前过滤,最后才是JOIN与聚合。理解这个顺序,你就能解释大多数优化手段的收益来源。
JOIN优化在ClickHouse中尤其特殊。它默认把右表加载进内存构建哈希表,因此右表必须小。分布式场景下还要考虑是否使用GLOBAL JOIN、是否把大表放左、是否用IN代替JOIN。金融场景中,行情表往往远大于证券主数据表,所以“小表在右”是必须条件。
二、方法流程:五类优化手段的落地顺序
- 分区裁剪:查询条件必须直接命中分区键,避免对分区键做函数包裹,否则无法裁剪。
- PREWHERE:把高选择性的过滤条件交给PREWHERE,先读少量列过滤,再读其余列。
- JOIN优化:小表放右,优先用IN或字典表替代JOIN,分布式大表考虑GLOBAL JOIN。
- 物化视图:把高频聚合结果预计算,查询时自动改写命中,适合固定维度的指标看板。
- 查询下推:谓词、聚合、LIMIT尽量下推到存储层,减少中间结果传输。
面试回答模板:先说明“我会先看EXPLAIN和query_log,确认扫描行数与分区命中情况”,再逐项给出优化动作,最后用扫描量或耗时变化量化收益。
三、案例场景:金融行情查询优化
假设有一张行情表 market_quote,按 trade_date 分区,按 symbol 排序。业务要查某只股票某天的分钟级行情,并关联证券主数据表 security_info 获取名称。
SELECT q.symbol, s.name, q.price, q.volume
FROM market_quote AS q
INNER JOIN security_info AS s ON q.symbol = s.symbol
PREWHERE q.trade_date = '2024-06-18' AND q.symbol = '600519'
WHERE q.price > 0
ORDER BY q.ts;
优化点:trade_date 命中分区裁剪;symbol 命中主键索引;PREWHERE提前过滤;security_info 是小表放右。若 security_info 只有几千行,可进一步用字典表替代JOIN,避免哈希表构建开销。
| 优化手段 | 适用场景 | 关键动作 | 常见误区 |
|---|---|---|---|
| 分区裁剪 | 按时间/日期查询 | 条件直接命中分区键 | 对分区键用函数 |
| PREWHERE | 高选择性过滤 | 先过滤再读列 | 与WHERE混用无收益 |
| JOIN优化 | 维表关联 | 小表在右、用IN/字典 | 大表放右导致OOM |
| 物化视图 | 固定维度聚合 | 预计算并自动改写 | 维度多变时失效 |
| 查询下推 | 分布式大表 | 谓词/聚合下推 | 忽略网络传输成本 |
四、误区与复盘:面试官最爱追问的点
误区一:认为PREWHERE一定比WHERE快。实际上当过滤条件选择性低时,PREWHERE反而增加一次读取。误区二:认为物化视图能覆盖所有查询。它只对匹配的聚合模式生效,维度变化后可能完全不命中。误区三:忽略分布式JOIN的数据传输成本,盲目使用GLOBAL JOIN。
复盘时建议用 system.query_log 查看 read_rows、read_bytes 和 memory_usage,对比优化前后。面试中能说出“我用query_log验证了扫描量下降80%”,比单纯罗列手段更有说服力。
加分回答:我会先判断查询是IO瓶颈还是CPU瓶颈。IO瓶颈优先分区裁剪和PREWHERE;CPU瓶颈优先物化视图和查询下推;JOIN内存问题优先调整右表或改用字典表。
掌握这五类手段后,你在面试中就能从“会写SQL”升级为“能解释执行计划并量化优化收益”,这正是金融数据岗位对ClickHouse能力的核心期待。