1. 为什么需要分析Query Plan和Profile?
如果你用过Apache Doris,肯定遇到过这种情况:一条看起来平平无奇的SQL,跑起来却慢得让人怀疑人生。你可能会想,是不是集群资源不够?是不是数据量太大了?但很多时候,问题的根源并不在硬件,而在于查询本身执行得“不聪明”。这就好比你要从北京去上海,明明有高铁直达,系统却给你规划了一条先飞到广州再转火车的路线,能不慢吗?
Query Plan(查询计划) 就是Doris为你的SQL语句规划的“出行路线图”。它决定了数据从哪里读取、以什么顺序连接、在哪里进行聚合计算。而 Profile 则是这条路线实际执行后的“行车记录仪”,它详细记录了每个步骤花了多少时间、处理了多少数据、消耗了多少资源。两者结合,就像你既有了地图,又有了导航的实时路况,能让你一眼就看出是哪个路口堵车了,哪段路在修。
我处理过不少性能问题,发现很多开发者和DBA一遇到慢查询,第一反应就是加机器、调参数,这其实是本末倒置。真正的优化,应该从理解查询是如何执行的开始。通过分析Plan和Profile,你不仅能快速定位瓶颈,比如是扫描了太多数据、连接顺序不合理,还是数据倾斜导致某个节点累死累活,更能从根本上理解Doris的优化器是如何工作的,从而写出更高效的SQL,甚至调整表结构来配合查询。这比盲目地“扔资源”要有效得多,也是从“会用”到“精通”的关键一步。
2. 快速上手:获取并解读你的第一个Query Plan
让我们从一个最简单的例子开始。假设我们有一张订单表 orders 和一张客户表 customers,我们想查询某个城市客户的订单总金额。SQL很简单:
SELECT c.city, SUM(o.amount) as total_amount
FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE c.city = '北京'
GROUP BY c.city;
想知道Doris打算怎么执行它吗?只需要在SQL前面加上 EXPLAIN:
EXPLAIN
SELECT c.city, SUM(o.amount) as total_amount
FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE c.city = '北京'
GROUP BY c.city;
执行后,你会得到一份看起来有点复杂的文本输出。别怕,我们一步步拆解。一个典型的Plan输出由多个 Fragment(执行片段) 组成。你可以把Fragment理解为一个可以在单台机器上独立执行的任务单元。Doris会把整个查询拆成多个Fragment,分发到不同的后端节点(BE)上并行执行。
我们来看一个简化后的Plan片段:
PLAN FRAGMENT 0
OUTPUT EXPRS:`c`.`city`, sum(`o`.`amount`)
PARTITION: UNPARTITIONED
RESULT SINK
|
5:EXCHANGE
PLAN FRAGMENT 1
PARTITION: HASH_PARTITIONED: `c`.`id`
STREAM DATA SINK
EXCHANGE ID: 05
UNPARTITIONED
4:AGGREGATE (update serialize)
| output: sum(`o`.`amount`)
| group by: `c`.`city`
|
3:HASH JOIN
| join op: INNER JOIN (BROADCAST)
| hash predicates: `o`.`customer_id` = `c`.`id`
|
|----2:EXCHANGE
|
1:OlapScanNode
TABLE: orders
PREAGGREGATION: ON
PREDICATES: `o`.`__DORIS_DELETE_SIGN__` = 0
PLAN FRAGMENT 2
PARTITION: HASH_PARTITIONED: `c`.`id`
STREAM DATA SINK
EXCHANGE ID: 02
UNPARTITIONED
0:OlapScanNode
TABLE: customers
PREAGGREGATION: ON
PREDICATES: `c`.`city` = '北京', `c`.`__DORIS_DELETE_SIGN__` = 0
怎么读这个Plan? 我教你一个“自底向上,从左到右”的方法。先找到叶子节点,也就是 OlapScanNode。你看,Fragment 2里有一个 OlapScanNode 在扫描 customers 表,并且注意 PREDICATES 部分,条件 c.city = '北京' 已经被下推(Pushdown)到存储层了。这意味着Doris在读取数据时就直接过滤掉了非北京的数据,而不是把所有数据读出来再过滤,这能极大减少IO。
然后看连接(JOIN)。在Fragment 1里,HASH JOIN 算子显示 <


399

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



