Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtp (Exim 4.72) (envelope-from ) id 1UB71S-0004DF-0j for pgsql-performance@arkaria.postgresql.org; Thu, 28 Feb 2013 17:11:58 +0000 Received: from localhost ([127.0.0.1] helo=postgresql.org) by malur.postgresql.org with smtp (Exim 4.72) (envelope-from ) id 1UB71Q-0003qx-F3 for pgsql-performance@arkaria.postgresql.org; Thu, 28 Feb 2013 17:11:56 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtp (Exim 4.72) (envelope-from ) id 1U8c4M-0001ai-PD for pgsql-performance@postgresql.org; Thu, 21 Feb 2013 19:44:39 +0000 Received: from mail-lb0-f180.google.com ([209.85.217.180]) by magus.postgresql.org with esmtp (Exim 4.72) (envelope-from ) id 1U8c4H-0005jS-Kr for pgsql-performance@postgresql.org; Thu, 21 Feb 2013 19:44:38 +0000 Received: by mail-lb0-f180.google.com with SMTP id q12so9394lbc.39 for ; Thu, 21 Feb 2013 11:44:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:sender:date:from:to:message-id:subject:x-mailer :mime-version:content-type:content-transfer-encoding :content-disposition; bh=c5sqM8qJ0Glu3XQnDnKEqAHXdQUZWHSegVDczTRL8us=; b=meOj1jXt4b288ESy4MWKVPvG4bd3uZXtlJQkG175/jCpyUQR6s9l9OqTIYgOQ0mweF iwt0AsDe4khq9K4BbLULIoOW6qhosuHZdsoyIQnzIQsN0BRx6MFpaQzDB/8QF14Y0D46 5W+7wreKc3vxCiFttBmJ+BYo5Y3FpyB89fUlBTwPkAm+f2guPI8JYrEJEOhYYuC2mXQB iFC4eXmvHNPou64tJJ7DywAeuBGLyyfpMTUIfpo9RhXKxP3dGT/n2YqLwnknUsH4Pyvw o76c1aD0EMVB7+5M+m4dUc4XLILomTLL2d+zp8L0tAiKXhpeITMvsTCcX8c9y5q/k6ci tT3w== X-Received: by 10.112.88.10 with SMTP id bc10mr10526480lbb.70.1361475872829; Thu, 21 Feb 2013 11:44:32 -0800 (PST) Received: from [10.0.1.14] ([217.14.6.25]) by mx.google.com with ESMTPS id fm8sm31951lbb.17.2013.02.21.11.44.29 (version=TLSv1 cipher=RC4-SHA bits=128/128); Thu, 21 Feb 2013 11:44:30 -0800 (PST) Date: Thu, 21 Feb 2013 20:44:27 +0100 From: Alexander Staubo To: pgsql-performance@postgresql.org Message-ID: Subject: Bad query plan with high-cardinality column X-Mailer: sparrow 1.6.4 (build 1178) MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Content-Disposition: inline X-Pg-Spam-Score: -2.6 (--) List-Archive: List-Help: List-ID: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: X-Mailing-List: pgsql-performance Precedence: bulk Sender: pgsql-performance-owner@postgresql.org I have a planner problem that looks like a bug, but I'm not familiar enough with how planner the works to say for sure. This is my schema: create table comments ( id serial primary key, conversation_id integer, created_at timestamp ); create index comments_conversation_id_index on comments (conversation_id); create index comments_created_at_index on comments (created_at); The table has 3.5M rows, and 650k unique values for "conversation_id", where the histogram goes up to 54000 rows for the most frequent ID, with a long tail. There are only 20 values with a frequency of 1000 or higher. The "created_at" column has 3.5M distinct values. Now, I have this query: select comments.id from comments where conversation_id = 3975979 order by created_at limit 13 This filters about 5000 rows and returns the oldest 13 rows. But the query is consistently planned wrong: Limit (cost=0.00..830.53 rows=13 width=12) (actual time=3174.862..3179.525 rows=13 loops=1) Buffers: shared hit=2400709 read=338923 written=21 -> Index Scan using comments_created_at_index on comments (cost=0.00..359938.52 rows=5634 width=12) (actual time=3174.860..3179.518 rows=13 loops=1) Filter: (conversation_id = 3975979) Rows Removed by Filter: 2817751 Buffers: shared hit=2400709 read=338923 written=21 Total runtime: 3179.553 ms It takes anywhere between 3 seconds and several minutes to run, depending on how warm the disk cache is. This is the correct plan and index usage: Limit (cost=6214.34..6214.38 rows=13 width=12) (actual time=25.471..25.473 rows=13 loops=1) Buffers: shared hit=197 read=4510 -> Sort (cost=6214.34..6228.02 rows=5471 width=12) (actual time=25.469..25.470 rows=13 loops=1) Sort Key: created_at Sort Method: top-N heapsort Memory: 25kB Buffers: shared hit=197 read=4510 -> Index Scan using comments_conversation_id_index on comments (cost=0.00..6085.76 rows=5471 width=12) (actual time=1.163..23.955 rows=5834 loops=1) Index Cond: (conversation_id = 3975979) Buffers: shared hit=197 read=4510 Total runtime: 25.500 ms Now, the problem for Postgres is obviously to estimate how many rows have a given "conversation_id" value, but it does have that number. I'm at a loss how to explain why it thinks scanning a huge index that covers the entire table will ever beat scanning a small index that has 17% of the table's values. It will consistently use the bad plan for higher-frequency values, and the good plan for lower-frequency values. If I run ANALYZE repeatedly, the planner will sometimes, oddly enough, choose the correct plan. This behaviour actually seems related to effective_cache_size; if it's set small (128MB), the planner will sometimes favour the good plan, but if large (2GB) it will consistently use the bad plan. I have bumped the statistics target up to 10000, but it does not help. I have also tried setting n_distinct for the column manually, since Postgres guesses it's 285k instead of 650k, but that does not help. What is odd is that the bad plan is really never correct in any situation for this query. It will *always* be better to branch off the "comments_conversation_id_index" index. Our environment: 9.2.2, tweaked memory parameters (work_mem, sort_mem, shared_buffers, effective_cache_size), not touched planner/cost parameters. Problem also exists on 9.2.3. (Please CC me as I'm not subscribed to the list. Thanks.) -- Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-performance