使用JSON字段时的查询优化

aco*_*hen 11 sql postgresql

在我的笔记本电脑上运行PostgreSQL 9.6.4,我有一个叫node有主键id字段和 properties::jsonb字段的表.

我在该properties字段上设置了GIN索引.

当我运行此查询时:

SELECT   n.*
FROM     node n
WHERE    node_type_id = '2'
AND      properties @> '{"slug":"wild-castles"}'::JSONB
ORDER BY n.id ASC OFFSET 0 LIMIT 10;
Run Code Online (Sandbox Code Playgroud)

在~5M行表上,获得答案大约需要20秒.查看解释计划,我发现查询优化器首先按主键对表进行排序,然后按properties字段过滤:

Limit  (cost=0.56..1517.94 rows=10 width=154)
  ->  Index Scan using node_pkey on node n  (cost=0.56..739571.11 rows=4874 width=154)
        Filter: ((properties @> '{"slug": "wild-castles"}'::jsonb) AND ((node_type_id)::text = '2'::text))
Run Code Online (Sandbox Code Playgroud)

但当我删除顺序时,我看到优化器使用索引按预期方式:

SELECT n.*
FROM   node n
WHERE  node_type_id = '2'
AND    properties @> '{"slug":"wild-castles"}'::JSONB
OFFSET 0 LIMIT 10;

Limit  (cost=93.77..127.10 rows=10 width=154)
  ->  Bitmap Heap Scan on node n  (cost=93.77..16338.56 rows=4874 width=154)
        Recheck Cond: (properties @> '{"slug": "wild-castles"}'::jsonb)
        Filter: ((node_type_id)::text = '2'::text)
        ->  Bitmap Index Scan on node_ix02  (cost=0.00..92.55 rows=4874 width=0)
              Index Cond: (properties @> '{"slug": "wild-castles"}'::jsonb)
Run Code Online (Sandbox Code Playgroud)

此外,一个简单的WHERE properties @> '{"slug":"wild-castles"}'::JSONB行为符合预期:

EXPLAIN SELECT   n.*
FROM     node n
WHERE    properties @> '{"slug":"wild-castles"}'::JSONB
;

Bitmap Heap Scan on node n  (cost=93.77..16326.38 rows=4874 width=154)
  Recheck Cond: (properties @> '{"slug": "wild-castles"}'::jsonb)
  ->  Bitmap Index Scan on node_ix02  (cost=0.00..92.55 rows=4874 width=0)
        Index Cond: (properties @> '{"slug": "wild-castles"}'::jsonb)
Run Code Online (Sandbox Code Playgroud)

所以我想我想知道为什么优化器不会先使用索引来过滤掉行,然后按id字段对它们进行排序?

san*_*wat 1

更改Planner 方法配置并强制 planner 不执行 seqscan

例如

      SET enable_seqscan = OFF;

       SELECT   n.*
        FROM     node n
               WHERE    node_type_id = '2'
               AND      properties @> '{"slug":"wild-castles"}'::JSONB
             ORDER BY n.id ASC OFFSET 0 LIMIT 10;
Run Code Online (Sandbox Code Playgroud)