27 second plan times

From: Gregory Stark <stark(at)enterprisedb(dot)com>
To: PostgreSQL-development Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: 27 second plan times
Date: 2007-04-20 20:17:54
Message-ID: 87647qajgt.fsf@oxford.xeocode.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers


Following up on some complaints we've had about extremely large plan times for
large partitioned tables I've been doing some profiling. I've constructed a
situation where it takes upwards of 30 seconds to plan a simple query like:

postgres=# explain select * from a where i between 999 and 1001;
QUERY PLAN
----------------------------------------------------------------------
Result (cost=0.00..8.63 rows=58 width=108)
-> Append (cost=0.00..8.63 rows=58 width=108)
-> Seq Scan on a (cost=0.00..1.01 rows=1 width=108)
Filter: ((i >= 999) AND (i <= 1001))
-> Seq Scan on a997 a (cost=0.00..1.27 rows=1 width=108)
Filter: ((i >= 999) AND (i <= 1001))
-> Seq Scan on a998 a (cost=0.00..1.27 rows=1 width=108)
Filter: ((i >= 999) AND (i <= 1001))
-> Seq Scan on a999 a (cost=0.00..1.27 rows=18 width=108)
Filter: ((i >= 999) AND (i <= 1001))
-> Seq Scan on a1000 a (cost=0.00..1.27 rows=18 width=108)
Filter: ((i >= 999) AND (i <= 1001))
-> Seq Scan on a1001 a (cost=0.00..1.27 rows=18 width=108)
Filter: ((i >= 999) AND (i <= 1001))
-> Seq Scan on a1002 a (cost=0.00..1.27 rows=1 width=108)
Filter: ((i >= 999) AND (i <= 1001))
(16 rows)

Time: 46324.627 ms

Table "a" is a parent table with 2,000 partitions each of which have 102
columns, two of which are covered by constraints of the form "WHERE i BETWEEN
90 AND 110".

The gprof output is pretty damning:

Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls s/call s/call name
62.58 203.76 203.76 21,474,937 0.00 0.00 SearchCatCache
9.01 233.08 29.32 1,923,789 0.00 0.00 list_nth_cell
5.34 250.46 17.38 2386465 0.00 0.00 pgstat_initstats
2.80 259.57 9.11 235691652 0.00 0.00 AllocSetAlloc
1.95 265.91 6.34 219852840 0.00 0.00 nocachegetattr
1.19 269.78 3.87 256569078 0.00 0.00 FunctionCall2
0.74 272.18 2.40 107923848 0.00 0.00 MemoryContextAllocZeroAligned

The SearchCatCache here is the one in get_attavgwidth called to estimate the
relation width. There are 200k attributes being measured here but I'm not
clear why it's causing 21M calls.

The first thing that comes to mind is that we're doing the
constraint_exclusion code *after* estimating the width of the relations we're
going to exclude. If we push the constraint exclusion up a few lines the
planning time goes down the 1.7s.

I think there's still a problem here with some kind of n^2 behaviour for
appends of very wide tables but I haven't quite nailed it yet. In any case is
there any reason not to make the following small change to move the constraint
exclusion ahead of the size estimates and index checks and save ourselves
potentially a lot of work?

Index: allpaths.c
===================================================================
RCS file: /home/stark/src/REPOSITORY/pgsql/src/backend/optimizer/path/allpaths.c,v
retrieving revision 1.161
diff -c -r1.161 allpaths.c
*** allpaths.c 22 Feb 2007 22:00:23 -0000 1.161
--- allpaths.c 20 Apr 2007 18:12:40 -0000
***************
*** 196,215 ****
static void
set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
- /* Mark rel with estimated output rows, width, etc */
- set_baserel_size_estimates(root, rel);
-
- /* Test any partial indexes of rel for applicability */
- check_partial_indexes(root, rel);
-
- /*
- * Check to see if we can extract any restriction conditions from join
- * quals that are OR-of-AND structures. If so, add them to the rel's
- * restriction list, and recompute the size estimates.
- */
- if (create_or_index_quals(root, rel))
- set_baserel_size_estimates(root, rel);
-
/*
* If we can prove we don't need to scan the rel via constraint exclusion,
* set up a single dummy path for it. (Rather than inventing a special
--- 196,201 ----
***************
*** 228,233 ****
--- 214,233 ----
return;
}

+ /* Mark rel with estimated output rows, width, etc */
+ set_baserel_size_estimates(root, rel);
+
+ /* Test any partial indexes of rel for applicability */
+ check_partial_indexes(root, rel);
+
+ /*
+ * Check to see if we can extract any restriction conditions from join
+ * quals that are OR-of-AND structures. If so, add them to the rel's
+ * restriction list, and recompute the size estimates.
+ */
+ if (create_or_index_quals(root, rel))
+ set_baserel_size_estimates(root, rel);
+
/*
* Generate paths and add them to the rel's pathlist.
*

--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Nicolas Barbier 2007-04-20 21:02:52 Re: Eliminating unnecessary left joins
Previous Message Neil Conway 2007-04-20 17:45:58 Re: Improving deadlock error messages