PostgreSQL 14 Explain计划:Limit/Result成本低但Index Scan成本高的原因
PostgreSQL 14查询执行计划成本解析
执行的SQL语句
explain analyze select min(ticketenti0_.created_at) as col_0_0_ from ticket ticketenti0_ where ticketenti0_.status=0 and ticketenti0_.is_spam_ticket='false' and ticketenti0_.portal_id=0000 and (ticketenti0_.group in (14 , 15 , 868 , 15 , 868 , 14));
生成的QUERY PLAN
Result (cost=224.59..224.60 rows=1 width=8) (actual time=9275.462..9275.464 rows=1 loops=1) InitPlan 1 (returns $0) -> Limit (cost=0.56..224.59 rows=1 width=8) (actual time=9275.460..9275.461 rows=0 loops=1) -> Index Scan using idx_ticket_created_at_status_portal_id_group on ticket ticketenti0_ (cost=0.56..696715.50 rows=3110 width=8) (actual time=9275.458..9275.458 rows=0 loops=1) Index Cond: ((created_at IS NOT NULL) AND (status = 0) AND (portal_id = 0000)) Filter: ((NOT is_spam_ticket) AND (group = ANY ('{14,15,868,15,868,14}'::bigint[]))) Rows Removed by Filter: 148605 Planning Time: 0.926 ms Execution Time: 9275.498 ms
疑问
为何Index Scan阶段成本(cost=0.56..696715.50)极高,但Limit阶段(cost=0.56..224.59)和Result阶段(cost=224.59..224.60)成本很低?目前判断该查询最终成本为224.59,不会占用过多CPU。
解答
- Index Scan的成本是全量扫描成本:这里的
0.56..696715.50是指如果把所有符合索引条件(created_at IS NOT NULL、status=0、portal_id=0000)的行全部扫描完成的总成本。PostgreSQL估算这类数据共有3110行,扫描全部的成本就是696715.50。 - Limit阶段截断了扫描范围:因为查询是求
min(created_at),PostgreSQL可以利用索引按created_at从小到大的顺序扫描,理论上只要找到第一条符合所有过滤条件的行就能停止扫描,不需要遍历全部符合索引条件的数据。所以Limit阶段估算只需要扫描到对应224.59成本的位置就能找到目标行,因此给出的成本上限是224.59。 - 最终估算成本以最上层阶段为准,但实际执行有偏差:Result阶段的成本基于Limit的结果计算,所以查询的估算成本确实是224.60左右。但实际执行时间高达9秒多,这是因为PostgreSQL的估算出现偏差——实际扫描了148605行才发现没有符合所有过滤条件的数据,所以虽然估算成本低,但实际执行耗时高,CPU占用也会比预期高。
内容的提问来源于stack exchange,提问作者Monika Yadav
相关产品推荐
相关产品推荐

