PostgreSQL - jsonb 索引未被应用

问题描述 投票:0回答:1

我有一个名为 users 的表,其中包含超过 200 万条记录,其结构如下:

create table users { 
   id integer not null,
   langs jsonb
}

langs 包含以下行数据:

    {"default_lang": "en", "custom_langs": ["en", "de"]}
    {"default_lang": "fr", "custom_langs": ["en", "de"]}
    {"default_lang": "pt", "custom_langs": ["en", "de", "fr"]}

想法是仅选择 default_lang 不包含在 custom_langs 中的行。所以,我能够像这样实现它:

SELECT id, langs
FROM  users
WHERE deleted=false and (langs->'default_lang' <@ (langs->'custom_langs') = false)

我还创建了以下索引:

    CREATE INDEX IF NOT EXISTS users_langs ON users 
    USING btree ((langs->'default_lang'), (langs->'custom_langs'))
    where langs->'default_lang' <@ (langs->'custom_langs') = false;

但是索引 users_langs 没有被应用。有想法该怎么解决这个吗。预先感谢。

这里是查询计划结果:

 HashAggregate  (cost=957627.32..1002810.29 rows=2536588 width=8) (actual time=17095.651..17096.210 rows=2233 loops=1)
   Output: id
   Group Key: users.id
   Planned Partitions: 64  Batches: 1  Memory Usage: 913kB
   Buffers: shared hit=47315 read=762287
   I/O Timings: read=11079.509
   ->  Seq Scan on public.users  (cost=0.00..821285.71 rows=2536588 width=8) (actual time=17.969..17086.833 rows=2233 loops=1)
         Output: id
         Filter: ((NOT users.deleted) AND (NOT ((users.langs -> 'default_langs'::text) <@ (users.langs -> 'custom_langs'::text))))
         Rows Removed by Filter: 2774667
         Buffers: shared hit=47315 read=762287
         I/O Timings: read=11079.509
 Settings: effective_cache_size = '8041608kB', jit = 'off'
 Query Identifier: -9027923201169468774
 Planning:
   Buffers: shared hit=427 read=32
   I/O Timings: read=4.276
 Planning Time: 5.737 ms
 Execution Time: 17096.749 ms

sql postgresql indexing jsonb b-tree
1个回答
0
投票

部分索引是可用的,如果禁用 seq 扫描(

set enable_seqscan=off
),则会使用它,但它不会正常使用,因为它被认为比全表扫描慢,因为行估计是太错了。

不幸的是,我不知道如何获得正确的估计,甚至无法通过对表达式使用自定义统计信息。我不知道为什么自定义统计不起作用。您可以使用第 3 方扩展 pg_hint_plan 强制进行索引扫描。

再玩一会儿之后,问题就出在

= false
。部分索引代码知道如何处理这个问题,但显然自定义统计代码不知道。如果您创建统计数据并分析表格:

create statistics lsdkfj on (langs->'default_lang' <@ (langs->'custom_langs')) 
from users;
analyze users;

那么统计数据将是正确的,但前提是查询是用

is false
而不是
= false
编写的。然后将使用索引(但前提是它也更改为
is false
)。

© www.soinside.com 2019 - 2024. All rights reserved.