postgresql - 为什么postgres选择了错误的执行计划

标签 postgresql postgresql-9.5 postgresql-performance

我有一个简单的查询

select count(*)
from taxi_order.ta_orders o
  inner join public.t_bases b on b.id = o.id_base
where o.c_phone2 = '012356789'
  and b.id_organization = 1
  and o.c_date_end < '2017-12-01'::date
group by date_trunc('month', o.c_date_end);

大多数时候,此查询运行速度很快,不到 100 毫秒,但有时对于某些 c_phone2、id_organization 组合,它开始运行非常慢,最多 4 秒。

快速案例的执行计划:

HashAggregate  (cost=7005.05..7005.62 rows=163 width=8)
  Group Key: date_trunc('month'::text, o.c_date_end)
  ->  Hash Join  (cost=94.30..7004.23 rows=163 width=8)
        Hash Cond: (o.id_base = b.id)
        ->  Index Scan using ix_ta_orders_c_phone2 on ta_orders o  (cost=0.57..6899.41 rows=2806 width=12)
              Index Cond: ((c_phone2)::text = $3)
              Filter: (c_date_end < $4)
        ->  Hash  (cost=93.26..93.26 rows=133 width=4)
              ->  Bitmap Heap Scan on t_bases b  (cost=4.71..93.26 rows=133 width=4)
                    Recheck Cond: (id_organization = $2)
                    ->  Bitmap Index Scan on ix_t_bases_id_organization  (cost=0.00..4.68 rows=133 width=0)
                          Index Cond: (id_organization = $2)

慢case的执行计划:

HashAggregate  (cost=6604.97..6604.98 rows=1 width=8)
  Group Key: date_trunc('month'::text, o.c_date_end)
  ->  Nested Loop  (cost=2195.33..6604.97 rows=1 width=8)
        ->  Bitmap Heap Scan on t_bases b  (cost=2.29..7.78 rows=3 width=4)
              Recheck Cond: (id_organization = $2)
              ->  Bitmap Index Scan on ix_t_bases_id_organization  (cost=0.00..2.29 rows=3 width=0)
                    Index Cond: (id_organization = $2)
        ->  Bitmap Heap Scan on ta_orders o  (cost=2193.04..2199.06 rows=3 width=12)
              Recheck Cond: (((c_phone2)::text = $3) AND (id_base = b.id) AND (c_date_end < $4))
              ->  BitmapAnd  (cost=2193.04..2193.04 rows=3 width=0)
                    ->  Bitmap Index Scan on ix_ta_orders_c_phone2  (cost=0.00..58.84 rows=3423 width=0)
                          Index Cond: ((c_phone2)::text = $3)
                    ->  Bitmap Index Scan on ix_ta_orders_id_base_date_end  (cost=0.00..2133.66 rows=83472 width=0)
                          Index Cond: ((id_base = b.id) AND (c_date_end < $4))

为什么查询计划器有时会选择如此缓慢无效的计划?

编辑

表的架构:

craete table taxi_order.ta_orders (
  id bigserial not null,
  id_base integer not null,
  c_phone2 character varying(30),
  c_date_end timestamp with time zone,
...
  CONSTRAINT pk_ta_orders PRIMARY KEY (id),
  CONSTRAINT fk_ta_orders_t_bases REFERENCES public.t_bases (id)
);

craete table public.t_bases (
  id serial not null,
  id_organization integer not null,
...
  CONSTRAINT pk_t_bases PRIMARY KEY (id)
);

ta_orders ~ 100M 行,t_bases ~ 2K 行。

编辑2

解释分析慢的情况:

HashAggregate  (cost=6355.29..6355.29 rows=1 width=8) (actual time=4075.847..4075.847 rows=1 loops=1)
  Group Key: date_trunc('month'::text, o.c_date_end)
  ->  Nested Loop  (cost=2112.10..6355.28 rows=1 width=8) (actual time=114.871..4075.803 rows=2 loops=1)
        ->  Bitmap Heap Scan on t_bases b  (cost=2.29..7.78 rows=3 width=4) (actual time=0.061..0.375 rows=133 loops=1)
              Recheck Cond: (id_organization = $2)
              Heap Blocks: exact=45
              ->  Bitmap Index Scan on ix_t_bases_id_organization  (cost=0.00..2.29 rows=3 width=0) (actual time=0.045..0.045 rows=133 loops=1)
                    Index Cond: (id_organization = $2)
        ->  Bitmap Heap Scan on ta_orders o  (cost=2109.81..2115.83 rows=3 width=12) (actual time=30.638..30.638 rows=0 loops=133)
              Recheck Cond: (((c_phone2)::text = $3) AND (id_base = b.id) AND (c_date_end < $4))
              Heap Blocks: exact=2
              ->  BitmapAnd  (cost=2109.81..2109.81 rows=3 width=0) (actual time=30.635..30.635 rows=0 loops=133)
                    ->  Bitmap Index Scan on ix_ta_orders_c_phone2  (cost=0.00..58.85 rows=3427 width=0) (actual time=0.032..0.032 rows=6 loops=133)
                          Index Cond: ((c_phone2)::text = $3)
                    ->  Bitmap Index Scan on ix_ta_orders_id_base_date_end  (cost=0.00..2050.42 rows=80216 width=0) (actual time=30.108..30.108 rows=94206 loops=133)
                          Index Cond: ((id_base = b.id) AND (c_date_end < $4))

快速案例的解释分析:

HashAggregate  (cost=7005.05..7005.62 rows=163 width=8) (actual time=0.927..0.928 rows=1 loops=1)
  Group Key: date_trunc('month'::text, o.c_date_end)
  ->  Hash Join  (cost=94.30..7004.23 rows=163 width=8) (actual time=0.903..0.913 rows=2 loops=1)
        Hash Cond: (o.id_base = b.id)
        ->  Index Scan using ix_ta_orders_c_phone2 on ta_orders o  (cost=0.57..6899.41 rows=2806 width=12) (actual time=0.591..0.604 rows=4 loops=1)
              Index Cond: ((c_phone2)::text = $3)
              Filter: (c_date_end < $4)
              Rows Removed by Filter: 2
        ->  Hash  (cost=93.26..93.26 rows=133 width=4) (actual time=0.237..0.237 rows=133 loops=1)
              Buckets: 1024  Batches: 1  Memory Usage: 13kB
              ->  Bitmap Heap Scan on t_bases b  (cost=4.71..93.26 rows=133 width=4) (actual time=0.058..0.196 rows=133 loops=1)
                    Recheck Cond: (id_organization = $2)
                    Heap Blocks: exact=45
                    ->  Bitmap Index Scan on ix_t_bases_id_organization  (cost=0.00..4.68 rows=133 width=0) (actual time=0.044..0.044 rows=133 loops=1)
                          Index Cond: (id_organization = $2)

我知道我可以为每个查询创建单独的索引以加快速度。但我想知道选择错误计划的原因是什么?我的统计数据有什么问题?

最佳答案

您必须向我们提供 EXPLAIN (ANALYZE, BUFFERS) 输出以获得明确的答案。

这两个计划的区别在于,第二个计划选择了嵌套循环连接,因为它估计只会从t_bases 中选择很少的行。由于您提示查询速度慢,该估计可能是错误的,导致在内表上循环太多。

尝试通过运行 ANALYZE 来改进您的表统计信息,也许在增加 default_statistics_target 之后。

ta_orders(c_phone2, id_base, c_date_end) 上的多列索引将缩短嵌套循环计划的执行时间。

关于postgresql - 为什么postgres选择了错误的执行计划,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48004218/

相关文章:

sql - 按年和月分组并获得一个月的最小值和日期

django - 如何防止此 PostgreSQL 查询中的全表扫描?

sql - 使用窗口函数计算每个事件行在给定间隔内事件的先前发生次数

ruby-on-rails - 按模型与特定属性的关联计数排序

Django 时间戳不一致

postgresql - 表约束中不存在 postgres 键

sql - 如何获取点击前的点击次数?

sql - 使用 'IN' 运算符组合两个查询时如何提高性能

postgresql - 如何将复杂类型数组的成员与 SELECT 进行比较?

PostgreSQL now() 值不改变函数中的值