我的表格targeting
包含marital_status
类型的列text[]
和data
类型的另一列jsonb
。这两列的内容是相同的,只是采用不同的格式(仅用于演示目的)。示例数据:
id | marital_status | data
----+--------------------------+---------------------------------------------------
1 | null | {}
2 | {widowed} | {"marital_status": ["widowed"]}
3 | {never_married,divorced} | {"marital_status": ["never_married", "divorced"]}
...
表中有超过690K的记录随机组合。
EXPLAIN ANALYZE SELECT marital_status
FROM targeting
WHERE marital_status @> '{widowed}'::text[]
通常需要<没有创建任何指数的900毫秒:
Seq Scan on targeting (cost=0.00..172981.38 rows=159061 width=28) (actual time=0.017..840.084 rows=158877 loops=1)
Filter: (marital_status @> '{widowed}'::text[])
Rows Removed by Filter: 452033
Planning time: 0.150 ms
Execution time: 845.731 ms
使用索引通常需要< 200毫秒(75%改进):
CREATE INDEX targeting_marital_status_idx ON targeting ("marital_status");
结果:
Index Only Scan using targeting_marital_status_idx on targeting (cost=0.42..23931.35 rows=159061 width=28) (actual time=3.528..143.848 rows=158877 loops=1)"
Filter: (marital_status @> '{widowed}'::text[])
Rows Removed by Filter: 452033
Heap Fetches: 0
Planning time: 0.217 ms
Execution time: 148.506 ms
EXPLAIN ANALYZE SELECT data
FROM targeting
WHERE (data -> 'marital_status') @> '["widowed"]'::jsonb
通常需要< 5,700毫秒没有创建任何指数(慢6倍以上!):
Seq Scan on targeting (cost=0.00..174508.65 rows=611 width=403) (actual time=0.095..5399.112 rows=158877 loops=1)
Filter: ((data -> 'marital_status'::text) @> '["widowed"]'::jsonb)
Rows Removed by Filter: 452033
Planning time: 0.172 ms
Execution time: 5408.326 ms
使用索引通常需要< 3,700毫秒(改善35%):
CREATE INDEX targeting_data_marital_status_idx ON targeting USING GIN ((data->'marital_status'));
结果:
Bitmap Heap Scan on targeting (cost=144.73..2482.75 rows=611 width=403) (actual time=85.966..3694.834 rows=158877 loops=1)
Recheck Cond: ((data -> 'marital_status'::text) @> '["widowed"]'::jsonb)
Rows Removed by Index Recheck: 201080
Heap Blocks: exact=33723 lossy=53028
-> Bitmap Index Scan on targeting_data_marital_status_idx (cost=0.00..144.58 rows=611 width=0) (actual time=78.851..78.851 rows=158877 loops=1)"
Index Cond: ((data -> 'marital_status'::text) @> '["widowed"]'::jsonb)
Planning time: 0.257 ms
Execution time: 3703.492 ms
text[]
列的性能更高,即使不使用索引?jsonb
列中添加索引只会使性能提高35%?jsonb
列上执行查找是否有更多的执行方式?答案 0 :(得分:1)
似乎是一个简单的问题。基本上你问的是怎么回事,
CREATE TABLE foo ( id int, key1 text );
快于
CREATE TABLE bar ( id int, jsonb foo );
@Craig在评论
中回答GIN索引通常比b树效率低,所以预计会有很多。
此架构中的空值也应为
SELECT jsonb_build_object('marital_status',ARRAY[null]);
jsonb_build_object
----------------------------
{"marital_status": [null]}
(1 row)
而不是{}
。 PostgreSQL采用了许多快捷方式来快速更新jsonb对象,并使索引空间有效。
如果没有任何意义,请查看此伪表。
CREATE TABLE foo ( id int, x text, y text, z text )
CREATE INDEX ON foo(x);
CREATE INDEX ON foo(y);
CREATE INDEX ON foo(z);
这里我们有三个btree索引。让我们看一下类似的表格。
CREATE TABLE bar ( id int, junk jsonb );
CREATE INDEX ON bar USING gin (junk);
INSERT INTO bar (id,junk) VALUES (1,$${"x": 10, "y": 42}$$);
要使bar
像foo
那样执行,我们需要两个双截面,这两个双截面都将比我们拥有的单个GIN索引大得多。如果你做了
INSERT INTO bar (id,junk) VALUES (1,$${"x": 10, "y": 42, "z":3}$$);
我们必须在z
上有另一个btree索引,这个索引也会很大。你可以看到我要去哪里。 jsonb
很棒,但索引和模式建模的复杂性并不与数据库并行。您不能将数据库缩减为jsonb列,发出CREATE INDEX
并期望获得相同的性能。
答案 1 :(得分:0)
这可能是使用jsonb_ops
(默认GIN索引策略)而不是jsonb_path_ops
的问题。
根据文件: https://www.postgresql.org/docs/9.6/static/datatype-json.html
尽管
jsonb_path_ops
运算符类仅支持使用@>
运算符的查询,但与默认运算符类jsonb_ops
相比,它具有显着的性能优势。对于相同的数据,jsonb_path_ops
索引通常比jsonb_ops
索引小得多,并且搜索的特异性更好,特别是当查询包含频繁出现在数据中的键时。因此,搜索操作通常比默认操作符类更好。
jsonb_ops
和jsonb_path_ops
GIN索引之间的技术差异是前者为数据中的每个键和值创建独立的索引项,而后者仅为每个值创建索引项数据。 [1]基本上,每个jsonb_path_ops索引项是值的哈希值和通向它的键;例如,索引{"foo": {"bar": "baz"}}
,将创建一个索引项,将foo,bar和baz中的所有三个合并到哈希值中。因此,查找此结构的包含查询将导致极其特定的索引搜索;但是根本没有办法找出foo是否作为关键。另一方面,jsonb_ops
索引将分别创建表示foo,bar和baz的三个索引项;然后,要进行包含查询,它将查找包含所有这三个项的行。虽然GIN索引可以相当有效地执行这样的AND搜索,但它仍然不如同等jsonb_path_ops
搜索更具体和更慢,特别是如果有非常多的行包含三个索引项中的任何一个。