postgresql - 为什么 Postgres 对 jsonb 列的查找如此缓慢?

标签 postgresql indexing postgresql-9.4 jsonb indices

我有一个表 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"]}
...

表中有69万多条记录,随机组合。

查找 text[] 列

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

在 jsonb 列上查找

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 列?

最佳答案

这似乎是个简单的问题。本质上你是在问为什么,

CREATE TABLE foo ( id int, key1 text );

CREATE TABLE bar ( id int, jsonb foo );

@Craig 在评论中回答了这个问题

GIN indexing is generally less efficient than a b-tree, so that much is expected.

该架构中的空值也应显示为

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}$$);

要使 barfoo 一样执行,我们需要两个 btree,它们都比我们拥有的单个 GIN 索引大。如果你这样做了

INSERT INTO bar (id,junk) VALUES (1,$${"x": 10, "y": 42, "z":3}$$);

我们必须在 z 上有另一个 btree 索引,这又将是巨大的。你可以看到我要去哪里。 jsonb 很棒,但索引和模式建模的复杂性与数据库不相称。您不能只将数据库缩减为 jsonb 列,发出 CREATE INDEX 并期望获得相同的性能。

关于postgresql - 为什么 Postgres 对 jsonb 列的查找如此缓慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41022627/

相关文章:

java - 错误!在 eclipse 上连接 Hibernate 和 Postgresql

spring - 尝试通过 websocket 转换和发送时出现空指针异常

javascript - 如何在 Node JS 中向 html 页面发送响应?

sql - PostgreSQL 高级记录计数

ruby-on-rails - 我想使用 Rails 控制台获取姓氏以 'x' 开头的用户数组

json - 给定类型名称的类型转换

mysql - 尝试加快大型表上的 mysql 查询速度

python - python中的sqlite3索引表

sql-server - 如何在与参数一起使用时优化 "OR"子句的使用 (SQL Server 2008)

postgresql - 无法连接到数据库 - 已发布 Postgres 作业计划(基于 Windows 的机器)