1. 从“慢查询”到“查询计划”:为什么你需要看懂Doris的执行蓝图?
你有没有遇到过这种情况?写了一条看起来挺简单的SQL,比如 SELECT * FROM user_behavior WHERE date='2024-01-01',结果等了十几秒甚至更久才出结果。心里嘀咕:数据量也不算特别大啊,索引也建了,怎么就慢了呢?这时候,光盯着SQL语句本身是找不到答案的。你得知道数据库引擎——比如我们这里说的Doris——它到底是怎么“干活”的。
这就好比你去餐厅点菜,你告诉服务员“来份宫保鸡丁”(这就是你的SQL)。但后厨怎么做这道菜,是先炸鸡丁还是先炒花生米,是用大火爆炒还是小火慢炖,这些流程你看不到。如果上菜慢了,你得去后厨看看是哪个环节卡住了:是备料慢了,还是灶台不够用?数据库的查询计划,就是这份“后厨操作流程图”。
在Doris里,查询计划尤其重要。因为它是一个分布式系统,数据分散在多个节点上。一条查询过来,Doris的查询优化器会把它拆解成多个可以并行执行的子任务(Fragment),这些子任务在不同的机器上跑,最后再把结果汇总给你。如果这个“拆解”和“执行”的计划没做好,就很容易出现“一部分机器累死,一部分机器闲死”的情况,查询自然就快不起来。
所以,学会看Doris的查询计划,是你从“只会写SQL”到“能调优SQL”的关键一步。它不再是黑盒,你能清晰地看到数据从哪里扫描、在哪里过滤、如何聚合、怎样在节点间传输。看懂了计划,你就能精准地定位瓶颈:是扫描了太多数据?是聚合计算太重?还是网络传输成了拖累?接下来,我们就手把手教你如何获取并解读这份“执行蓝图”。
2. 庖丁解牛:如何获取与解读Doris查询计划?
拿到查询计划是分析的第一步。Doris提供了两种非常直观的方式,一种叫 EXPLAIN,另一种叫 EXPLAIN GRAPH。我习惯先用 EXPLAIN 看文本逻辑,再用 EXPLAIN GRAPH 看图形化流程,两者结合,心里就特别有谱。
2.1 使用 EXPLAIN:查看文本执行计划
这跟MySQL的用法很像,直接在SQL前加上 EXPLAIN 就行。我们来看一个最简单的例子:
EXPLAIN SELECT siteid, citycode, SUM(pv) FROM user_visit GROUP BY siteid, citycode;
执行后,你会得到一大段文本输出。别慌,我们拆开看。输出的核心是 PLAN FRAGMENT。Doris会把整个执行计划分成若干个片段(Fragment),每个片段可以独立在不同的后端节点(BE)上并行执行。片段之间通过 EXCHANGE 节点来交换数据。
比如,一个典型的聚合查询计划可能长这样:
PLAN FRAGMENT 0
OUTPUT EXPRS: `siteid`, `citycode`, sum(`pv`)
PARTITION: UNPARTITIONED
RESULT SINK
|
| 3:AGGREGATE (merge finalize)
| | output: sum(sum(`pv`))
| | group by: `siteid`, `citycode`
| | cardinality=-1
| |
| 2:EXCHANGE
|
PLAN FRAGMENT 1
OUTPUT EXPRS:
PARTITION: RANDOM
STREAM DATA SINK
EXCHANGE ID: 02
UNPARTITIONED
|
| 1:AGGREGATE (update serialize)
| | output: sum(`pv`)
| | group by: `siteid`, `citycode`
| | cardinality=1
| |
| 0:OlapScanNode
| TABLE: user_visit
| PREAGGREGATION: ON
| partitions=4/4
| rollup: user_visit
| tabletRatio=32/32
| tabletList=10001,10002...
| cardinality=10000000
| avgRowSize=45.0
| numNodes=10
我来给你翻译一下:
- PLAN FRAGMENT 1:这是执行的第一阶段。
0:OlapScanNode表示它在扫描user_visit表。下面那些partitions、tabletRatio信息非常关键,它告诉你数据扫描的范围。比如partitions=4/4表示所有4个分区都参与了扫描,如果这里写partitions=1/4,那说明分区裁剪生效了,只扫了1个分区,这是好事。tabletRatio=32/32表示所有32个tablet(数据分片)都被扫描了。 - 1:AGGREGATE (update serialize):扫描完数据,立刻在本地(每个BE上)进行初步的聚合(
sum(pv)),并按siteid, citycode分组。注意这里的cardinality=1是个估计值,实际要看分组后的行数。 - STREAM DATA SINK ... EXCHANGE ID: 02:第一阶段的结果,会通过一个数据流发送器(DataStreamSink)发送出去,交换ID是02。
- PLAN FRAGMENT 0:这是执行的第二阶段(最终阶段)。
2:EXCHANGE节点接收来自第一阶段的数据。 - 3:AGGREGATE (merge finalize):这里进行最终的聚合合并。因为第一阶段已经在每个BE上做了预聚合,这里只需要把各个BE上相同

1778

被折叠的 条评论
为什么被折叠?



