PostgreSQL:在jsonb列上查询-索引不能使其更快

Eri*_*ang 2 postgresql indexing jsonb

在中有一个表Postgresql 9.6jsonb与关系表相比,对列的查询要慢,并且GIN在表上添加索引并不能使其更快。

表:

-- create table
create table dummy_jsonb (
    id serial8,
    data jsonb,
    primary key (id)
);

-- create index
CREATE INDEX dummy_jsonb_data_index ON dummy_jsonb USING gin (data);
-- CREATE INDEX dummy_jsonb_data_index ON dummy_jsonb USING gin (data jsonb_path_ops);
Run Code Online (Sandbox Code Playgroud)

产生资料:

-- generate data,
CREATE OR REPLACE FUNCTION dummy_jsonb_gen_data(n integer) RETURNS integer AS $$
DECLARE
    i integer:=1;
    name varchar;
    create_at varchar;
    json_str varchar;
BEGIN
    WHILE i<=n LOOP
        name:='dummy_' || i::text;
        create_at:=EXTRACT(EPOCH FROM date_trunc('milliseconds', now())) * 1000;
        json_str:='{
                 "name": "' || name || '",
                 "size": ' || i || ',
                 "create_at": ' || create_at || '
               }';

        insert into dummy_jsonb(data) values
        (json_str::jsonb
        );
        i:= i + 1;
    END LOOP;

    return n;
END;
$$ LANGUAGE plpgsql;

-- call function,
select dummy_jsonb_gen_data(1000000);

-- drop function,
DROP FUNCTION IF EXISTS dummy_jsonb_gen_data(integer);
Run Code Online (Sandbox Code Playgroud)

查询:

select * from dummy_jsonb
where data->>'name' like 'dummy_%' and data->>'size' >= '500000'
order by data->>'size' desc
offset 50000 limit 10;
Run Code Online (Sandbox Code Playgroud)

测试结果:

  • 在慢速虚拟机上,查询需要1.8秒。
  • 添加或删除索引没有什么不同。
  • 使用更改为索引杜松子酒jsonb_path_ops也没有什么不同。

问题:

  • 是否可以提高索引或sql的速度来加快查询速度?
  • 如果不是,这意味着在pg中使用关系表在这种情况下更合适吗?
  • 而且,在我的测试中,mongodb性能更好,这是否意味着mongodb更适合此类存储和查询?

a_h*_*ame 7

引用手册

对于jsonb支持在查询默认GIN操作符类顶级密钥存在运营商??&以及?|运营商和路径/价值存在运营商@>[...]非默认GIN操作符类jsonb_path_ops支持索引的@>唯一运营商。

您的查询使用LIKE和进行字符串比较>(开始时可能不正确),GIN索引均不支持。

但是,即使在指数(data ->> 'name')不会被用于条件data->>'name' like 'dummy_%',因为这是真正的所有,因为每个名称开头的行dummy

您可以在名称上创建常规btree索引:

CREATE INDEX ON dummy_jsonb ( (data ->> 'name') varchar_pattern_ops);
Run Code Online (Sandbox Code Playgroud)

如果条件足够严格,将使用该方法,例如:

where data->>'name' like 'dummy_9549%'
Run Code Online (Sandbox Code Playgroud)

如果您需要查询大小,可以在其上创建索引((data ->> 'size')::int),然后使用类似以下的内容:

where (data->>'size')::int >= 500000
Run Code Online (Sandbox Code Playgroud)

但是,您对limit和的使用offset将始终迫使数据库读取所有行,对其进行排序并限制结果。这永远不会很快。您可能想阅读这篇文章以获得更多信息,为什么限制/偏移不是很有效。


JSON是关系世界的一个很好的补充,但前提是您使用得当。如果一行不需要动态属性,则使用标准列和数据类型。尽管Postgres对JSON的支持非常好,但这并不意味着应该对它使用所有的东西,只是因为它是当前的炒作。Postgres仍然是一个关系数据库,应该这样使用。


无关,但是:您生成测试数据的功能可以简化为单个SQL语句。您可能尚未意识到generate_series()此类功能:

insert into dummy_jsonb(data)
select jsonb_build_object('name', 'dummy_'||i, 
                          'size', i::text, 
                          'created_at', (EXTRACT(EPOCH FROM date_trunc('milliseconds', clock_timestamp())) * 1000)::text)
from generate_series(1,1000000) as t(i);
Run Code Online (Sandbox Code Playgroud)