Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtp (Exim 4.80) (envelope-from ) id 1YG0E7-0007My-VJ for pgsql-performance@arkaria.postgresql.org; Tue, 27 Jan 2015 07:06:20 +0000 Received: from localhost ([127.0.0.1] helo=postgresql.org) by malur.postgresql.org with smtp (Exim 4.80) (envelope-from ) id 1YG0E7-00042K-El for pgsql-performance@arkaria.postgresql.org; Tue, 27 Jan 2015 07:06:19 +0000 Received: from makus.postgresql.org ([2001:4800:1501:1::229]) by malur.postgresql.org with esmtps (TLS1.2:DHE_RSA_AES_256_CBC_SHA256:256) (Exim 4.80) (envelope-from ) id 1YG0E5-0003yu-JM for pgsql-performance@postgresql.org; Tue, 27 Jan 2015 07:06:17 +0000 Received: from mx-out.zmailcloud.com ([192.198.85.98] helo=mx-out-1.zmailcloud.com) by makus.postgresql.org with esmtp (Exim 4.80) (envelope-from ) id 1YG0E1-00034V-7G for pgsql-performance@postgresql.org; Tue, 27 Jan 2015 07:06:14 +0000 Received: from smtp.01.com (smtp.01.com [10.10.0.43]) by mx-out-1.zmailcloud.com (Postfix) with ESMTP id 42C1E563E45 for ; Tue, 27 Jan 2015 01:06:12 -0600 (CST) Received: from localhost (localhost [127.0.0.1]) by smtp-out-2.01.com (Postfix) with ESMTP id 34237601AC for ; Tue, 27 Jan 2015 01:06:12 -0600 (CST) X-Virus-Scanned: amavisd-new at smtp-out-2.01.com Received: from smtp.01.com ([127.0.0.1]) by localhost (smtp-out-2.01.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id oRG--uATBftP for ; Tue, 27 Jan 2015 01:06:12 -0600 (CST) Received: from smtp.01.com (localhost [127.0.0.1]) by smtp-out-2.01.com (Postfix) with ESMTP id 13F61601F8 for ; Tue, 27 Jan 2015 01:06:12 -0600 (CST) Received: from localhost (localhost [127.0.0.1]) by smtp-out-2.01.com (Postfix) with ESMTP id F1EDC601B2 for ; Tue, 27 Jan 2015 01:06:11 -0600 (CST) X-Virus-Scanned: amavisd-new at smtp-out-2.01.com Received: from smtp.01.com ([127.0.0.1]) by localhost (smtp-out-2.01.com [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id tGuVCEPFXN24 for ; Tue, 27 Jan 2015 01:06:11 -0600 (CST) Received: from [192.168.47.107] (50-0-164-164.dsl.dynamic.fusionbroadband.com [50.0.164.164]) by smtp-out-2.01.com (Postfix) with ESMTPSA id 8B2E8601AC for ; Tue, 27 Jan 2015 01:06:11 -0600 (CST) Message-ID: <54C738E1.8080405@agliodbs.com> Date: Mon, 26 Jan 2015 23:06:09 -0800 From: Josh Berkus Organization: PostgreSQL Experts Inc. User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.3.0 MIME-Version: 1.0 To: pgsql-performance@postgresql.org Subject: working around JSONB's lack of stats? Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit X-Pg-Spam-Score: -1.9 (-) 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 Folks, Currently, JSONB fields don't have statistics, and estimate a flat 1% selectivity. This can result in poor query plans, and I'm wondering if anyone has a suggested workaround for this short of hacking a new selectivity function. For example, take the common case of using JSONB to hold a list of "tags" for tagging documents: Table "public.doc_tags_json" Column | Type | Modifiers --------+---------+----------- doc_id | integer | tags | jsonb | Indexes: "doc_tags_json_doc_id_idx" UNIQUE, btree (doc_id) "doc_tags_json_tags_idx" gin (tags) This query: select doc_id from doc_tags_json where tags @> '[ "math", "physics" ]' order by doc_id desc limit 25; Uses this plan: QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------------------------------------- Limit (cost=0.43..709.79 rows=25 width=4) (actual time=24.529..340.499 rows=25 loops=1) -> Index Scan Backward using doc_tags_json_doc_id_idx on doc_tags_json (cost=0.43..283740.95 rows=10000 width=4) (actual time=24.528..340.483 rows=25 loops=1) Filter: (tags @> '["math", "physics"]'::jsonb) Rows Removed by Filter: 1011878 Planning time: 0.090 ms Execution time: 340.528 ms It does this because it expects @> '["math", "physics"]' to match 10,000 rows, which means that it expects to only scan 25,000 entries in the doc_id index to return the top 25. However, the matching condition is much rarer than it thinks, so it's actually far faster to use the index on the JSONB column: drop index doc_tags_json_doc_id_idx; QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------------------- Limit (cost=10517.08..10517.14 rows=25 width=4) (actual time=7.594..7.602 rows=25 loops=1) -> Sort (cost=10517.08..10542.08 rows=10000 width=4) (actual time=7.593..7.596 rows=25 loops=1) Sort Key: doc_id Sort Method: top-N heapsort Memory: 26kB -> Bitmap Heap Scan on doc_tags_json (cost=92.90..10234.89 rows=10000 width=4) (actual time=6.733..7.475 rows=257 loops=1) Recheck Cond: (tags @> '["math", "physics"]'::jsonb) Heap Blocks: exact=256 -> Bitmap Index Scan on doc_tags_json_tags_idx (cost=0.00..90.40 rows=10000 width=0) (actual time=6.695..6.695 rows=257 loops=1) Index Cond: (tags @> '["math", "physics"]'::jsonb) Planning time: 0.093 ms Execution time: 7.632 ms On a normal column, I'd raise n_distinct to reflect the higher selecivity of the search terms. However, since @> uses contsel, n_distinct is ignored. Anyone know a clever workaround I don't currently see? -- Josh Berkus PostgreSQL Experts Inc. http://pgexperts.com -- Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-performance