Incorrect choice of Nested Loop for a skewed distribution

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi All,

With PostgreSQL 10 and 11, the planner doesn't use the lists of most common values to determine the selectivity of "=" for Nested Loop as it does for a normal inner join in eqjoinsel_inner(). Incorrect choice of a nested loops join strategy causes poor query performance.
To demonstrate it one can make the following test case:

  create table t(f integer not null,g integer not null);
  create table u(f integer not null,g integer not null);
  create sequence s cache 1000;
  insert into t select 0,s from (select nextval('s') as s) as d;
  insert into t select 0,s from (select nextval('s') as s) as d;
  insert into t select 0,s from (select nextval('s') as s from t,t t1,t t2) as d;   insert into t select 0,s from (select nextval('s') as s from t,t t1,t t2,t t3) as d;
  insert into t(f,g) select g,f from t;
  insert into u select * from t;
  create index t_f on t(f);
  vacuum analyze;

The columns f and g of both tables t and u have a skewed distribution: 10010 values of 0 and 10010 unique values starting from 1.
Let's see query plan for the join of t and u:

  explain analyze select * from t,u where t.f=u.f and t.g=u.g;

                                                  QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.29..7629.95 rows=25055030 width=16) (actual time=0.042..22540.629 rows=20020 loops=1)    ->  Seq Scan on u  (cost=0.00..289.20 rows=20020 width=8) (actual time=0.011..3.025 rows=20020 loops=1)    ->  Index Scan using t_f on t  (cost=0.29..0.36 rows=1 width=8) (actual time=0.565..1.125 rows=1 loops=20020)
         Index Cond: (f = u.f)
         Filter: (u.g = g)
         Rows Removed by Filter: 5004
 Planning Time: 0.394 ms
 Execution Time: 22542.639 ms

After dropping the index
  drop index t_f;
we obtain much better query plan (without Nested Loop):

                                                   QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
 Merge Join  (cost=3439.09..442052.26 rows=25055030 width=16) (actual time=15.708..32.735 rows=20020 loops=1)
   Merge Cond: ((t.f = u.f) AND (t.g = u.g))
   ->  Sort  (cost=1719.54..1769.59 rows=20020 width=8) (actual time=8.189..10.189 rows=20020 loops=1)
         Sort Key: t.f, t.g
         Sort Method: quicksort  Memory: 1707kB
         ->  Seq Scan on t  (cost=0.00..289.20 rows=20020 width=8) (actual time=0.012..2.958 rows=20020 loops=1)    ->  Sort  (cost=1719.54..1769.59 rows=20020 width=8) (actual time=7.510..9.459 rows=20020 loops=1)
         Sort Key: u.f, u.g
         Sort Method: quicksort  Memory: 1707kB
         ->  Seq Scan on u  (cost=0.00..289.20 rows=20020 width=8) (actual time=0.008..2.748 rows=20020 loops=1)
 Planning Time: 0.239 ms
 Execution Time: 34.585 ms

Using MCV lists in var_eq_non_const() would prevent from choosing Nested Loop in such cases.

Regards,
Oleg Kharin






[Postgresql General]     [Postgresql PHP]     [PHP Users]     [PHP Home]     [PHP on Windows]     [Kernel Newbies]     [PHP Classes]     [PHP Books]     [PHP Databases]     [Yosemite]

  Powered by Linux