【发布时间】:2021-05-04 12:47:46
【问题描述】:
为了在 jsonb 列中搜索特定键,我想在该列上创建一个索引。
使用:Postgres 10.2
忽略一些不相关的列,我的表 animals 包含这些列(省略了一些不相关的列):
animalid PK number
location (text)
type (text)
name (text)
data (jsonb) for eg: {"age": 2, "tagid": 11 }
我需要根据:location、type 和 tagId 进行搜索。喜欢:
where location = ? and type = 'cat' and (data ->> 'tagid') = ?
其他要点:
- 只有猫类型的动物会有标签 ID,这是现在添加的新动物类型。
- 与其他类型的动物相比,整个表中“猫”的数量会更少。
- 表很大,有数百万行 - 并且是分区的。
如何确保搜索速度很快?我考虑过的选项:
- 制作一个单独的表cats来存储:
animal_id、location、tagId(虽然FK到分区的父表是不可能的) - 在
location、type和 jsonb 键上创建索引。 - 创建一个新的(索引的)列
tagId- 对于除猫以外的所有动物,该列都为空。
我确实在表的其他列上有一个索引 - 但对于如何创建索引以根据 tagid 快速搜索猫有点困惑。有什么建议吗?
更新(忽略分区):
(在分区表上测试)
所以我决定采用 Erwin 建议的选项并尝试创建索引
CREATE INDEX ON animals_211 (location, ((data->>'tagid')::uuid)) WHERE type = 'cat';
并尝试对查询进行解释(使用分区表保持简单):
explain select * from animals_211 a
where a.location = 32341
and a.type = 'cat'
and (data->>'tagid')::uuid = '5e54c1d9-3ea0-4bca-81d6-1000d90cc42c'
从结果来看,它似乎没有使用创建的索引并进行顺序扫描:
Seq Scan on animals_211 e (cost=0.00..121.70 rows=1 width=327) |
Filter: ((location = 32341) AND ((type)::text = 'cat'::text) AND (((data ->> 'tagid'::text))::uuid = '5e54c1d9-3ea0-4bca-81d6-1000d90cc42c'::uuid
UPDATE 2(不使用部分索引)
它似乎是部分索引,没有它 - 它似乎工作:
CREATE INDEX tag_id_index ON animals_211 (location, type, ((data->>'tagid')::uuid))
当我制定解释计划时:
Index Scan using tag_id_index on animals_211 e (cost=0.28..8.30 rows=1 width=327)
Index Cond: ((location = 32341) AND ((type)::text = 'cat'::text) AND (((data ->> 'tagid'::text))::uuid = '5e54c1d9-3ea0-4bca-81d6-1000d90cc42c'::uuid))
【问题讨论】:
-
您的更新令人困惑。部分索引在父表上,但 Postgres 10 不支持父表上的索引。您应该会看到一个错误消息。我围绕该主题在我的答案中添加了更多内容。我建议将最佳索引问题与最佳分区问题分开......
-
@ErwinBrandstetter 能够创建索引,没有错误消息。但它没有出现在表的索引列表中。我同意,让我们将分区排除在外(我需要为所有分区手动执行此操作) - 我尝试专门在一个分区上创建索引。创建了索引,但解释计划显示它仍然不使用索引。我只尝试了位置+类型只是为了查看并且有效。所以不知何故,这似乎是 jsonb 索引的问题?
-
部分索引肯定可以工作。也许你需要运行
ANALYZE。考虑一下我添加到答案中的注释和链接。
标签: postgresql indexing database-design jsonb postgresql-performance