Postgresql:查询 jsonb 列 - 索引并不能使其更快

标签 postgresql indexing jsonb

Postgresql 9.6 中有一个表,在 jsonb 列上的查询比关系表慢,并且在上添加了一个 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);

生成数据:

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

查询:

select * from dummy_jsonb
where data->>'name' like 'dummy_%' and data->>'size' >= '500000'
order by data->>'size' desc
offset 50000 limit 10;

测试结果:

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

问题:

  • 是否有可能使查询更快,改进索引或 sql?
  • 如果不是,这是否意味着在 pg 中关系表在这种情况下更合适?
  • 而且,在我的测试中,mongodb 表现更好,这是否意味着 mongodb 更适合此类存储和查询?

最佳答案

Quote from the manual

The default GIN operator class for jsonb supports queries with top-level key-exists operators ?, ?& and ?| operators and path/value-exists operator @> [...] The non-default GIN operator class jsonb_path_ops supports indexing the @> operator only.

您的查询使用 LIKE 和字符串与 > 的比较(这可能一开始就不正确),GIN 索引都不支持这些。

但即使 (data ->> 'name') 上的索引也不会用于条件 data->>'name' like 'dummy_%' 对于 所有 行都是如此,因为每个名称都以 dummy 开头。

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

CREATE INDEX ON dummy_jsonb ( (data ->> 'name') varchar_pattern_ops);

如果条件足够严格,将使用哪个,例如:

where data->>'name' like 'dummy_9549%'

如果你需要查询大小,你可以在 ((data ->> 'size')::int) 上创建一个索引,然后使用这样的东西:

where (data->>'size')::int >= 500000

但是,您使用 limitoffset 将始终强制数据库读取所有行,对它们进行排序并限制结果。这永远不会很快。您可能想阅读 this article有关为什么限制/偏移不是很有效的更多信息。


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

关于Postgresql:查询 jsonb 列 - 索引并不能使其更快,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48092484/

相关文章:

php - Laravel 5 - SQLSTATE [22P02] - postgres 的无效文本表示

sql - GROUP BY 包括 0,其中不存在

postgresql - pg_rman 恢复

JSONB 字段中对象的 Postgresql 查询数组

postgresql - 如何更新 PostgreSQL 中的 jsonb 列字段?

mysql - SQL 统计字段中的单词数

mysql - 优化mysql索引

python - 自定义索引的数据结构

c# - NEST 2.3.1 (Elastic Search) 创建索引出错

sql - Postgres JSONB 从数据数组中进行选择