Postgresql 与其他列一起索引JSONB键
为了在Postgresql 与其他列一起索引JSONB键,postgresql,indexing,database-design,jsonb,postgresql-performance,Postgresql,Indexing,Database Design,Jsonb,Postgresql Performance,为了在jsonb列中搜索特定键,我想在该列上创建一个索引 使用:Postgres 10.2 忽略一些不相关的列,我的表中有这些列(忽略一些不相关的列): 我需要根据以下内容进行搜索:位置,类型和标记ID。比如: where location = ? and type = 'cat' and (data ->> 'tagid') = ? 其他要点: 只有猫类型的动物才会有标签id,这是一个新的动物类型正在添加 与其他类型的动物相比,整张桌子上“猫”的数量更少 这张桌子很大,有数百万
jsonb
列中搜索特定键,我想在该列上创建一个索引
使用:Postgres 10.2
忽略一些不相关的列,我的表中有这些列(忽略一些不相关的列):
我需要根据以下内容进行搜索:位置
,类型
和标记ID
。比如:
where location = ? and type = 'cat' and (data ->> 'tagid') = ?
其他要点:
- 只有猫类型的动物才会有标签id,这是一个新的动物类型正在添加
- 与其他类型的动物相比,整张桌子上“猫”的数量更少
- 这张桌子很大,有数百万行,而且是分区的
animal\u id
,location
,tagId
(尽管无法将FK扩展到分区的父表)位置
、类型
和jsonb键上创建索引tagId
——该列对于除猫以外的所有动物都为空tagid
的cat搜索快速,我有点困惑。有什么建议吗
更新(忽略分区):
(在分区表上测试)
因此,我决定按照欧文的建议使用这个选项,并尝试创建一个索引
CREATE INDEX ON animals_211 (location, ((data->>'tagid')::uuid)) WHERE type = 'cat';
并尝试对查询进行解释(使用分区表保持简单):
从结果来看,它似乎没有使用创建的索引,而是进行顺序扫描:
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
更新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))
基础(忽略分区)
根据你的三个“要点”,我建议对一个表达方式进行一次修改:
创建动物索引((数据->>'tagid'))
其中type='cat';
用于避免对同一表进行并发写访问时出现锁定问题
Postgres还为部分索引收集特定的统计数据,这有助于查询计划人员获得适当的估计注意如果在创建索引后立即测试索引,则需要手动运行ANALYZE
(或VACUUM ANALYZE
),然后才能启动autovacuum
。见:
tagid
确实是text
以外的其他数据类型,则还可以强制转换表达式以进行更多优化。见:
tagid
存储UUID值。阅读:
创建动物索引(((数据->>'tagid')::uuid))--!
其中type='cat';
需要在(数据->>'tagid')::uuid
周围添加一组括号,以使语法明确无误。和一个匹配的查询:
选择*
来自动物
其中位置=32341
和类型='cats'
和(数据->>'tagid')::uuid='5e54c1d9-3ea0-4bca-81d6-1000d90cc42c';--!
或者-取决于每个谓词的选择性以及可能的查询变体-包括位置
,使其成为多列索引:
创建动物索引(位置,((数据->>'tagid')::uuid))
其中type='cat';
或者,如果您有查询,但没有对位置进行筛选,请先执行tagid
。见:
tagid
索引就可以了。双赢
如果可能,将json键data->>'tagid'
作为专用列。(就像您认为的选项3.)在不适用的地方可以为null,null存储非常便宜。使存储和索引更便宜,但查询更简单
tagid
:
对于声明式分区,分区必须具有与分区表完全相同的列集,而对于表继承,子表可能具有父表中不存在的额外列
听起来很合适。但遗传已经不再受博士后的青睐,所以在这样做之前我会三思而后行
无论是声明性还是继承性,如果在单独的分区中有所有的“cat”,那么非部分索引都会起作用,显然:
在cats上创建索引(位置,((数据->>'tagid')::uuid));
查询的目标可以是分区cats
,而不是父表:
选择*
来自猫
其中位置=32341
和(数据->>'tagid')::uuid='5e54c1d9-3ea0-4bca-81d6-1000d90cc42c';
以父表为目标也应该有效。(对10级博士后不太清楚。)
选择*
来自动物
其中type='cat'
和位置=32341
和(数据->>'tagid')::uuid='5e54c1d9-3ea0-4bca-81d6-1000d90cc42c';
但要为此激活。手册:
请注意,分区修剪仅由定义的约束驱动
隐式地通过分区键,而不是通过索引的存在。
因此,没有必要在键列上定义索引
所有其他分区都应该被删减,然后你就可以删除了
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))