Received: from localhost (unknown [200.46.204.183]) by postgresql.org (Postfix) with ESMTP id C20AB9F99CB for ; Fri, 21 Sep 2007 10:37:59 -0300 (ADT) Received: from postgresql.org ([200.46.204.71]) by localhost (mx1.hub.org [200.46.204.183]) (amavisd-maia, port 10024) with ESMTP id 90728-08 for ; Fri, 21 Sep 2007 10:37:40 -0300 (ADT) X-Greylist: from auto-whitelisted by SQLgrey-1.7.5 Received: from outmail128174.authsmtp.com (outmail128174.authsmtp.com [62.13.128.174]) by postgresql.org (Postfix) with ESMTP id 86C199F98D3 for ; Fri, 21 Sep 2007 10:37:40 -0300 (ADT) Received: from outmail128181.authsmtp.net (outmail128181.authsmtp.net [62.13.128.181]) by punt2.authsmtp.com (8.13.8/8.13.8/Kp) with ESMTP id l8LDbbwD084394; Fri, 21 Sep 2007 14:37:37 +0100 (BST) Received: from [192.168.0.3] (85-211-21-1.dyn.gotadsl.co.uk [85.211.21.1]) (authenticated bits=0) by mail.authsmtp.com (8.13.8/8.13.8/Kp) with ESMTP id l8LDbaKb066086; Fri, 21 Sep 2007 14:37:36 +0100 (BST) Subject: Re: Searching for the cause of a bad plan From: Simon Riggs To: Csaba Nagy Cc: postgres performance list In-Reply-To: <1190376744.4661.208.camel@PCD12478> References: <1190369024.4661.173.camel@PCD12478> <1190372365.4202.14.camel@ebony.site> <1190374166.4661.194.camel@PCD12478> <1190374478.4202.20.camel@ebony.site> <1190376744.4661.208.camel@PCD12478> Content-Type: text/plain Date: Fri, 21 Sep 2007 14:36:17 +0100 Message-Id: <1190381777.4202.50.camel@ebony.site> Mime-Version: 1.0 X-Mailer: Evolution 2.8.2 Content-Transfer-Encoding: 7bit X-Server-Quench: d0f37347-6847-11dc-b86a-001185d377ca X-AuthRoute: OCdxZQATClZOTQEd DAteCiNZVAwpPBRK HVkIKg5MJUcNSQVJ NksadBtFagRbflxB HGQLW1NEUVl7WmR/ aA0fZQBDYEtPQQdr TklWQFdNFEs0CWB1 VE9lABl0cQVOfjB0 bUBkECRbCkd9dkJ6 X0hcEWobZGY0O31N WEYJagNUcQJCe0tG bVcsBT1vNG8XDRoV JSEUBRUEdQ5SIT5e QgALIhocRlxDAj4x DxsLAC4iVUoLDyx7 JRUqYkYYFUB5 X-Authentic-SMTP: 61633235383639.squirrel.dmpriest.net.uk:752/Kp X-Report-SPAM: If SPAM / abuse - report it at: http://www.authsmtp.com/abuse X-Virus-Status: No virus detected - but ensure you scan with your own anti-virus system! X-Virus-Scanned: Maia Mailguard 1.0.1 X-Archive-Number: 200709/363 X-Sequence-Number: 26908 On Fri, 2007-09-21 at 14:12 +0200, Csaba Nagy wrote: > On Fri, 2007-09-21 at 12:34 +0100, Simon Riggs wrote: > > On Fri, 2007-09-21 at 13:29 +0200, Csaba Nagy wrote: > > > > > > Can you plans with/without LIMIT and with/without cursor, for both b1 > > > > and b2? > > > > > > The limit is unfortunately absolutely needed part of the query > > > > Understood, but not why I asked... > > > Well, the same query without limit goes: OK, thanks. > I'm not sure what you mean without cursor, maybe not using prepare ? Sorry, misread that. ======================= I think I understand now: The cost of the LIMIT is being applied, but in slightly the wrong way. The cost of the Nested Loop node is reduced by the fraction of LIMIT/(number of expected rows), which is only an approximation of what we're doing. In Plan 2 this leads to the wildly wrong estimate that each row costs 49,851 cost units to retrieve, which is about x50 wrong. In Plan 3 that approximation leads to a more reasonable cost, so this works in Plan 3, but doesn't in Plan 2. What we should do is push down the effect of the LIMIT so that the cost of the Index Scan on ta reflects the fact that it returns only 10 rows. It correctly expects 388638 rows that match the value requested, but it is not retrieving all of them. The executor handles the query efficiently but the cost model doesn't reflect what the executor actually does and so we pick the wrong plan. Pushing down the LIMIT would only be possible when LIMIT has a constant value at plan time, but that seems like most of the time to my eyes. The plan estimates should look like this for Plan 2 (marked **) Limit (cost=0.00..XXXX rows=10 width=1804) -> Nested Loop (cost=0.00..XXXXX rows=10 width=1804) -> Index Scan using pk_table_a on table_a ta (cost=0.00..**11.96** rows=**10** width=16) Index Cond: (a = $1) -> Index Scan using pk_table_b2 on table_b2 tb (cost=0.00..3.77 rows=1 width=1788) Index Cond: (ta.b = tb.b) Incidentally, the way out of this is to improve the stats by setting stats target = 1000 on column a of ta. That will allow the optimizer to have a better estimate of the tail of the distribution of a, which should then be more sensibly reflected in the cost of the Index Scan. That doesn't solve the actual problem, but should help in your case. Plans copied below for better clarity: Plan 2: db> explain analyze execute test_001(31855344); QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------ Limit (cost=0.00..498511.80 rows=10 width=1804) (actual time=17.729..21.672 rows=2 loops=1) -> Nested Loop (cost=0.00..1794642.48 rows=36 width=1804) (actual time=17.729..21.671 rows=2 loops=1) -> Index Scan using pk_table_a on table_a ta (cost=0.00..324880.88 rows=388638 width=16) (actual time=0.146..0.198 rows=2 loops=1) Index Cond: (a = $1) -> Index Scan using pk_table_b2 on table_b2 tb (cost=0.00..3.77 rows=1 width=1788) (actual time=10.729..10.731 rows=1 loops=2) Index Cond: (ta.b = tb.b) Total runtime: 21.876 ms Plan 3: db> explain analyze execute test_001(31855344); QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------ Limit (cost=0.00..853.14 rows=10 width=325) (actual time=20.117..28.104 rows=2 loops=1) -> Nested Loop (cost=0.00..2024323.48 rows=23728 width=325) (actual time=20.116..28.101 rows=2 loops=1) -> Index Scan using pk_table_a on table_a ta (cost=0.00..327561.01 rows=388684 width=16) (actual time=0.023..0.027 rows=2 loops=1) Index Cond: (a = $1) -> Index Scan using pk_table_b1 on table_b1 tb (cost=0.00..4.35 rows=1 width=309) (actual time=14.032..14.034 rows=1 loops=2) Index Cond: (ta.b = tb.b) Total runtime: 28.200 ms -- Simon Riggs 2ndQuadrant http://www.2ndQuadrant.com