根据以下SQL内容,解析两条PostgreSQL explain执行计划:

- CREATE TABLE orders( order_nr int, amount decimal(12,2), billed boolean );
- CREATE INDEX orders_unbilled_index ON orders (order_nr) WHERE billed is not true;
- insert into orders select t.seq, t.seq*2.44, true from generate_series(1,500000) as t(seq); 
- insert into orders select t.seq, t.seq*2.44, false from generate_series(500001,509000) as t(seq);
------------------------------------------------------------------------------------------
- explain SELECT * FROM orders WHERE billed is not true AND order_nr < 10000;
                                 QUERY PLAN                                        
------------------------------------------------------------------------------------------
 Bitmap Heap Scan on orders  (cost=177.04..3880.78 rows=76139 width=21)
   Recheck Cond: ((order_nr < 10000) AND (billed IS NOT TRUE))
   ->  Bitmap Index Scan on orders_unbilled_index  (cost=0.00..158.01 rows=76139 width=0)
         Index Cond: (order_nr < 10000)
(4 rows)
------------------------------------------------------------------------------------------
- explain SELECT * FROM orders WHERE billed is true AND order_nr < 10000;
 QUERY PLAN                          
--------------------------------------------------------------
 Seq Scan on orders  (cost=0.00..8462.40 rows=76139 width=21)
   Filter: ((billed IS TRUE) AND (order_nr < 10000))
(2 rows)

根据提供的实际EXPLAIN执行计划,来进行详细解析:

一、第一条查询解析

EXPLAIN SELECT * FROM orders WHERE billed is not true AND order_nr < 10000;

执行计划:

Bitmap Heap Scan on orders  (cost=177.04..3880.78 rows=76139 width=21)
   Recheck Cond: ((order_nr < 10000) AND (billed IS NOT TRUE))
   ->  Bitmap Index Scan on orders_unbilled_index  (cost=0.00..158.01 rows=76139 width=0)
         Index Cond: (order_nr < 10000)

深度解析:

  1. Bitmap Index Scan:首先使用部分索引orders_unbilled_index进行位图索引扫描
  2. 索引条件order_nr < 10000 - 在索引层面过滤
  3. Bitmap Heap Scan:将索引扫描结果转换为位图,然后在堆中进行精确查找
  4. Recheck Cond:需要重新检查条件,确保数据准确性
  5. 成本分析:总成本3880.78,索引扫描成本仅158.01,效率很高
  6. 行数估计:优化器估计返回76,139行(实际应该很少,这是估计错误)

二、第二条查询解析

EXPLAIN SELECT * FROM orders WHERE billed is true AND order_nr < 10000;

执行计划:

Seq Scan on orders  (cost=0.00..8462.40 rows=76139 width=21)
   Filter: ((billed IS TRUE) AND (order_nr < 10000))

深度解析:

  1. Seq Scan:选择全表顺序扫描
  2. 原因
    • 部分索引orders_unbilled_index不包含billed = true的记录
    • billed = true的记录占98.2%,选择性很差
    • 没有其他合适的索引可用
  3. Filter:在扫描过程中应用过滤条件
  4. 成本分析:总成本8462.40,比第一条查询高很多
  5. 行数估计:同样估计76,139行(估计不准确)

关键发现和优化器行为分析

1. 行数估计问题

  • 两条查询都估计返回76,139行,但实际:
    • 第一条:实际应该很少(只有billed is not trueorder_nr < 10000的记录)
    • 第二条:实际应该有约10,000行(所有order_nr < 10000的记录,因为前10,000条都是billed = true

2. 索引使用有效性

  • 第一条查询:完美命中部分索引,使用Bitmap Index Scan效率很高
  • 第二条查询:无法使用部分索引,只能全表扫描

3. 执行计划选择合理性

  • 优化器做出了正确选择:
    • 对稀疏条件(billed is not true)使用索引
    • 对稠密条件(billed is true)使用全表扫描

4. 潜在优化建议

-- 如果需要频繁查询 billed = true 的记录,可以创建另一个部分索引
CREATE INDEX orders_billed_index ON orders (order_nr) WHERE billed is true;

总结

执行计划显示PostgreSQL优化器能够:

  1. 正确识别和使用部分索引
  2. 根据数据分布选择最优访问路径
  3. 对选择性好的条件使用索引扫描
  4. 对选择性差的条件使用顺序扫描

虽然行数估计有偏差,但执行计划的选择是完全合理的。

需要注意:

  • PostgreSQL⽀持带任意谓词的部分索引,条件是只涉及被索引表的字段,但是,谓词必须和那些希望从该索引中获益的查询中的WHERE条件相匹配,
  • 准确地说,只有在系统能够识别出该查询的WHERE条件简单地包含了该索引的谓词时,这个部分索引才能⽤于该查询。
  • PostgreSQL还没有智能到可以完全识别那些形式不同但数学上意义相等的谓词。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐