使用JSON字段时的查询优化

时间:2017-09-10 21:16:00

标签: sql postgresql

在我的笔记本电脑上运行PostgreSQL 9.6.4,我有一个名为properties的表,其中包含主键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; 字段和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)) 字段上设置了GIN索引。

当我运行此查询时:

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)

在~5M行表上大约需要20秒才能得到答案。查看解释计划,我发现查询优化器首先按主键对表进行排序,然后按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)

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

id

此外,简单exports.lp = function (str, strm) { return function prependLog() { var args = Array.from(arguments); var hasNonWhitespace = args.some(function (a) { var str = String(a); return str.length > 0 && /\S/g.test(str); }); if (hasNonWhitespace) { strm.write(str); } args.forEach(function (s, i) { String(s).split('\n').forEach(function (s, i) { if (i < 1) { strm.write(s + ' '); } else { strm.write('\n' + str + s); } }); }); strm.write('\n'); }; }; 的行为与预期相符:

const {lp} = require('log-prepend');
const fn = lp(' [foobar] ', process.stdout);


fn('\n');
fn();
fn();
fn('','','');
fn('log1', 'log2\n3',4,5 + '\n55');
fn('a','b','c');

所以我想我想知道为什么优化器不会先使用索引来过滤掉行,然后按 [foobar] [foobar] log1 log2 [foobar] 34 5 [foobar] 55 [foobar] a b c 字段对它们进行排序?

2 个答案:

答案 0 :(得分:1)

更改Planner Method Configuration并强制刨刀不要执行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;

答案 1 :(得分:1)

根据我的经验,您有时不得不欺骗查询规划器以使其表现良好,并且需要进行一些调整和摆弄......

我会尝试运行它来查看它的执行情况:

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