我有一个名为 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
部分索引是可用的,如果禁用 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
)。