PostgreSQL JSONB @> 运算符是否等于 ->''=?

Kok*_*zzu 7 postgresql-9.4

jsonb_column @> '{"key":value}'::jsonb运营商等于jsonb_column->'key' = value?在将使用的结果、性能和索引方面?

dez*_*zso 13

我创建了一个小测试来检查这个:

CREATE TABLE jsonb_test (id serial, data jsonb);
Run Code Online (Sandbox Code Playgroud)

然后插入一些生成的数据:

INSERT INTO jsonb_test (data) 
SELECT row_to_json(x.*)::jsonb 
  FROM (SELECT i AS bla, 
               ARRAY[i, i+1, i+2] AS foo, 
               ARRAY[('v' || i::text, i), ('v' || (i * 10)::text, i)] AS bar
          FROM generate_series(1,100000) t(i)
       ) x;
Run Code Online (Sandbox Code Playgroud)

让我们在那里找到一些东西(版本 1):

SELECT * FROM jsonb_test WHERE data @> '{"bla": 545}'::jsonb;
Run Code Online (Sandbox Code Playgroud)

执行计划看起来像

 Seq Scan on jsonb_test  (cost=0.00..7604.89 rows=369 width=36) (actual time=0.425..47.600 rows=1 loops=1)
   Filter: (data @> '{"bla": 545}'::jsonb)
   Rows Removed by Filter: 99999
   Buffers: shared hit=2997 dirtied=676
 Planning time: 0.078 ms
 Execution time: 47.692 ms
Run Code Online (Sandbox Code Playgroud)

如果我对您提议的其他版本(版本 2)也这样做(操作员从->更改->>为 我认为您的意思):

SELECT * FROM jsonb_test WHERE data ->> 'bla' = '545';
Run Code Online (Sandbox Code Playgroud)

我有一个计划

 Seq Scan on jsonb_test  (cost=0.00..8526.47 rows=1843 width=36) (actual time=0.613..34.657 rows=1 loops=1)
   Filter: ((data ->> 'bla'::text) = '545'::text)
   Rows Removed by Filter: 99999
   Buffers: shared hit=2997
 Planning time: 0.116 ms
 Execution time: 34.727 ms
Run Code Online (Sandbox Code Playgroud)

反复运行它们,我发现执行时间之间没有显着差异。难怪 - 这里昂贵的操作是顺序扫描,而两种不同的过滤条件在这方面应该差别不大。

现在我添加了一个 GIN 索引data

CREATE INDEX ON jsonb_test USING gin (data);
Run Code Online (Sandbox Code Playgroud)

现在执行查询时,版本 2与上述相同,而版本 1有一个新计划:

 Bitmap Heap Scan on jsonb_test  (cost=28.77..372.05 rows=100 width=36) (actual time=0.081..0.083 rows=1 loops=1)
   Recheck Cond: (data @> '{"bla": 545}'::jsonb)
   Rows Removed by Index Recheck: 2
   Heap Blocks: exact=1
   Buffers: shared hit=10
   ->  Bitmap Index Scan on jsonb_test_data_idx  (cost=0.00..28.75 rows=100 width=0) (actual time=0.062..0.062 rows=3 loops=1)
         Index Cond: (data @> '{"bla": 545}'::jsonb)
         Buffers: shared hit=9
 Planning time: 0.084 ms
 Execution time: 0.122 ms
Run Code Online (Sandbox Code Playgroud)

这已经表明这两个版本并不完全等效 -jsonb列上的 GIN 索引仅支持其中之一(版本 1)。

可以定义另一个索引来帮助版本 2

CREATE INDEX ON jsonb_test ((data->>'bla'));
Run Code Online (Sandbox Code Playgroud)

现在版本 2给出了以下内容:

 Bitmap Heap Scan on jsonb_test  (cost=12.17..1323.49 rows=500 width=36) (actual time=0.065..0.070 rows=1 loops=1)
   Recheck Cond: ((data ->> 'bla'::text) = '545'::text)
   Heap Blocks: exact=1
   Buffers: shared hit=1 read=2
   ->  Bitmap Index Scan on jsonb_test_expr_idx  (cost=0.00..12.04 rows=500 width=0) (actual time=0.046..0.046 rows=1 loops=1)
         Index Cond: ((data ->> 'bla'::text) = '545'::text)
         Buffers: shared read=2
 Planning time: 0.480 ms
 Execution time: 0.133 ms
Run Code Online (Sandbox Code Playgroud)

在这个实验中,如果索引正确,我没有看到两者之间的巨大性能差异。但是,您应该考虑以下几点

由于可能为每个项目插入许多键,因此插入 GIN 索引可能会很慢。因此,对于表中的批量插入,建议删除 GIN 索引并在完成批量插入后重新创建它。

通常说 BTree 索引比 GIN 索引维护成本更低(意味着表上的数据更改具有更小的开销)。通过阅读,情况并非总是如此 - 您必须在自己的数据集上测试这些解决方案。例如,我无法判断一组非常复杂的jsonb值是否更喜欢这个或那个。

笔记: