Received: from malur.postgresql.org ([2a02:16a8:dc51::56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.89) (envelope-from ) id 1g9gDi-0008R2-Fv for pgsql-performance@arkaria.postgresql.org; Tue, 09 Oct 2018 00:49:54 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1g9gDg-0002qG-MZ for pgsql-performance@arkaria.postgresql.org; Tue, 09 Oct 2018 00:49:52 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.89) (envelope-from ) id 1g9gDg-0002q9-C4 for pgsql-performance@lists.postgresql.org; Tue, 09 Oct 2018 00:49:52 +0000 Received: from mail-it1-x133.google.com ([2607:f8b0:4864:20::133]) by magus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1g9gDd-0006Aq-Kq for pgsql-performance@lists.postgresql.org; Tue, 09 Oct 2018 00:49:51 +0000 Received: by mail-it1-x133.google.com with SMTP id i191-v6so231281iti.5 for ; Mon, 08 Oct 2018 17:49:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=telsasoft-com.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to:user-agent; bh=7ltB1DWFu3T0/SDYPnnfn7lV3xxL89HeNA4YVtVc1lQ=; b=AfiF+tPtHjHvR3QJ/SIMXrWSSPgP820X1U9ljjEtbxXWwM6LDO2Uw4N4drg9slbvwg H2GzFp9O+oKDtZfp2HgWRFLhGlP516nSfD9pirezWrgXFZfr2YFIW7Hz4q6w9g2Af8BO 0RzDme35UYKOCupwHSUN7jqnvVHaLmk4KyBECZMcP/0bkvacpzWCPOr2K5UCGVj2lRf1 ZE+frHQXqDeJXtU6Y/6gb8zFIC0fVUK8kJKXzK4q8pBDvHW3latJPYryGwaaYQcL5Jpl ASIppGq9cQKwoCugQToKIIC/VdbQ+ju58yrY3CzBr/nbyifeTqJHL7H+z7im1XtJNjsV gM9A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=7ltB1DWFu3T0/SDYPnnfn7lV3xxL89HeNA4YVtVc1lQ=; b=mW7QkZq8wN58ztqzkr4wkkRy9AEDNlT1DfC/ZwabSnl+YojWH0HmARspS3ODT241qJ l6yy0X6MBdXc2oLuf7uR3whbP8wqFHS1TW+sM95kP3efm98nlloyVqR8iGYddqKORJmE yex/PFsPmmyKHlnGyL2pge00JrTxowIMK9DcMDI3R7hn4SxbbVp8sBXBTZz9HB8afo28 DQ/uJRgG6/FCSltt18CJTcSAS0ddSjuB4QllI7+B1xZvTxPNQSSrJLXskYDHVzuySm66 Itf6ULdouAijeiwToJr/P5MUbJXfHRF2zYRnPwkhWEVeGSHQJglk2zWAAru5Lp1eCN66 XhNQ== X-Gm-Message-State: ABuFfohrMK3coAWzbLB8eqc9JQ+77JiM88u5tyYVdw7AyT5xp/XiFH/9 mfyBCSgFfx09WEk/KmKxss1Srw== X-Google-Smtp-Source: ACcGV62c8Khkil86FS/szuBdF9kx9tqK02imbqnwXEC+j4oKRZcIFO6FS20x/xQZD4YJjryHUpQUZw== X-Received: by 2002:a24:db05:: with SMTP id c5-v6mr235213itg.44.1539046186467; Mon, 08 Oct 2018 17:49:46 -0700 (PDT) Received: from pryzbyj (charmander.telsasoft.com. [50.244.222.1]) by smtp.gmail.com with ESMTPSA id q185-v6sm5198843itc.30.2018.10.08.17.49.45 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 08 Oct 2018 17:49:45 -0700 (PDT) Received: by pryzbyj (Postfix, from userid 1000) id BE36D804FC9; Mon, 8 Oct 2018 19:49:44 -0500 (CDT) Date: Mon, 8 Oct 2018 19:49:44 -0500 From: Justin Pryzby To: James Coleman Cc: pgsql-performance@lists.postgresql.org Subject: Re: Partial index plan/cardinality costing Message-ID: <20181009004944.GO871@telsasoft.com> References: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.23 (2014-03-12) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk Please don't cross-post to lists. >insert into s(status, action_at, m_fk) >select > ( CASE WHEN series.n % 100 < 80 THEN > (ARRAY['E', 'C'])[(series.n % 2) + 1] > ELSE > (ARRAY['P', 'PD', 'A'])[((random() * 3)::integer % 3) + 1] > END > ), > ( > CASE WHEN series.n % 100 < 80 THEN > '2018-09-07'::date + ((series.n % 365 - 365)::text || ' day')::interval > ELSE > '2018-09-07'::date + (((random() * 30)::integer % 30 - 4)::text || ' day')::interval > END > ), > (select m.pk from m limit 1) >from generate_series(1, 500000) series(n); > I have two partial indexes: > - s_pk_action_at on s(pk, action_at) where status in ('P', 'PD', 'A') > - s_action_at_pk on s(action_at, pk) where status in ('P', 'PD', 'A') > - How is s_pk_action_at ever efficient to scan? Given that the highest > cardinality (primary key) column is first, wouldn't an index scan > effectively have to scan the entire index? The index probably IS inefficient to scan (you could see that if you force an bitmap index scan on s_pk_action_at)...but because of leading pkey column, the HEAP is read sequentially, and the planner knows that the heap will be read in order of its leading column. Reading the entire index is less expensive than reading most of the table (maybe nonsequentially). This is the 2nd effect Jeff Janes likes to point out: high correlation means 1) sequential reads; *and*, 2) a smaller fraction of the table needs to be accessed to read a given number of tuples. > - Why does index scan on s_action_at_pk reads over 2x as many blocks as the > bitmap heap scan with the same index? Maybe because of heap pages accessed multiple times (not sequentially), since correlation is small on this table loaded with "modulus"-style insertions. pryzbyj=# SELECT attname, correlation FROM pg_stats WHERE tablename='s' ; attname | correlation -----------+------------- pk | 1 status | 0.340651 action_at | 0.00224239 m_fk | 1 ..so each index tuple is accessing a separate heap page. If you create non-partial index and CLUSTER on action_at_idx, then: pryzbyj=# SELECT attname, correlation FROM pg_stats WHERE tablename='s' ; attname | correlation -----------+------------- pk | 0.00354867 status | 0.420806 action_at | 1 m_fk | 1 Nested Loop (cost=1907.03..6780.65 rows=11038 width=8) (actual time=2.241..17.839 rows=8922 loops=1) Join Filter: (s.m_fk = m.pk) Buffers: shared hit=115 read=53 -> Seq Scan on m (cost=0.00..1.01 rows=1 width=8) (actual time=0.009..0.011 rows=1 loops=1) Filter: (status = 'A'::text) Buffers: shared hit=1 -> Bitmap Heap Scan on s (cost=1907.03..6641.66 rows=11038 width=16) (actual time=2.222..9.032 rows=8922 loops=1) Recheck Cond: ((action_at <= '2018-09-06'::date) AND (status = ANY ('{P,PD,A}'::text[]))) Filter: (status = ANY ('{A,P}'::text[])) Rows Removed by Filter: 4313 Heap Blocks: exact=114 Buffers: shared hit=114 read=53 -> Bitmap Index Scan on s_action_at_pk (cost=0.00..1904.27 rows=82647 width=0) (actual time=2.185..2.186 rows=13235 loops=1) Index Cond: (action_at <= '2018-09-06'::date) Buffers: shared read=53 Also, I don't think it matters here, but action_at and status are correlated. Planner would think that they're independent. I don't think it's related to other issues, but also note the rowcount estimate is off: -> Bitmap Index Scan on s_action_at_pk (cost=0.00..1258.02 rows=82347 width=0) (actual time=1.026..1.026 rows=13402 loops=1) Index Cond: (action_at <= '2018-09-06'::date) Justin