【问题标题】:Postgres: Searching over jsonb array field is slowPostgres:搜索 jsonb 数组字段很慢
【发布时间】:2020-05-03 06:15:37
【问题描述】:

我们正在为我们的一个项目更改 DB(PostgreSQL 10.11) 结构。其中一项更改是将 uuid[] 类型的字段(称为“areasoflawid”)移动到 jsonb 字段(称为“数据”)中。

所以,我们有一个如下所示的表格:

CREATE TABLE public.documents
(
  id serial,
  areasoflawid uuid[], --the field to be moved into the ‘data’
  data jsonb,
  ….
)

我们不会更改数组的值或其结构。 即documents.data->'metadata'->'areaoflawids'包含与documents.areasoflawid相同的项目) 数据迁移后,“data”字段中存储的JSON结构如下:

{
   ...
   "metadata": {
      ...
      "areaoflawids": [
         "e34e0ee5-78e0-4d92-9186-ac69c109408b",
         "b3af9163-d910-4d19-8f40-0602b75c25b0",
         "50dc7fd8-ebdf-4cd2-bcab-b8d755fe96e8",
         "8955c062-363f-4a1a-ac3c-d1c2ffe96c9b",
         "bdb79f9f-4539-45f5-ac82-92baaf915f6c"
      ],
      ....
   },
   ...
}

因此,在迁移数据之后,我们开始对 jsonb 字段相关的查询进行基准测试,并发现搜索数组字段documents.data->'metadata'->'areaoflawids' 比搜索uuid[] 字段documents.areasoflawid 花费的时间要长得多.

这里是查询:

--search over jsonb array field, takes 6.2 sec, returns 13615 rows
SELECT id FROM documents WHERE data->'metadata'->'areaoflawids' @> '"e34e0ee5-78e0-4d92-9186-ac69c109408b"'

--search over uuid[] field, takes 600ms, returns 13615 rows
SELECT id FROM documents WHERE areasoflawid @> ARRAY['e34e0ee5-78e0-4d92-9186-ac69c109408b']::uuid[]

这是 jsonb 字段的索引:

CREATE INDEX test_documents_aols_gin_idx
  ON public.documents
  USING gin
  (((data -> 'metadata'::text) -> 'areaoflawids'::text) jsonb_path_ops);

这是执行计划:

EXPLAIN ANALYZE SELECT id FROM documents WHERE data->'metadata'->'areaoflawids' @> '"e34e0ee5-78e0-4d92-9186-ac69c109408b"'


"Bitmap Heap Scan on documents  (cost=6.31..390.78 rows=201 width=4) (actual time=2.297..5859.886 rows=13614 loops=1)"
"  Recheck Cond: (((data -> 'metadata'::text) -> 'areaoflawids'::text) @> '"e34e0ee5-78e0-4d92-9186-ac69c109408b"'::jsonb)"
"  Heap Blocks: exact=4859"
"  ->  Bitmap Index Scan on test_documents_aols_gin_idx  (cost=0.00..6.30 rows=201 width=0) (actual time=1.608..1.608 rows=13614 loops=1)"
"        Index Cond: (((data -> 'metadata'::text) -> 'areaoflawids'::text) @> '"e34e0ee5-78e0-4d92-9186-ac69c109408b"'::jsonb)"
"Planning time: 0.133 ms"
"Execution time: 5862.807 ms"

对 jsonb 字段的其他查询以可接受的速度工作,但这种特定的搜索比在分隔字段上的搜索慢约 10 倍。我们期望它会慢一点,但还不错。我们考虑将这个“areasoflawid”字段作为一个单独的字段保留的选项,但我们肯定更愿意将它移动到 json 中。我一直在使用不同的索引和操作(也使用?和?|),但搜索仍然很慢。任何帮助表示赞赏!

【问题讨论】:

  • 你试过WHERE data->'metadata'->'areaoflawids' ? 'e34e0ee5-78e0-4d92-9186-ac69c109408b'
  • 嗨,是的,它对我没有多大帮助...要使用此操作,我必须更改索引:``` CREATE INDEX test_documents_aols_gin_idx ON public.documents USING gin (((data -> '元数据'::text) -> 'areaoflawids'::text)); ``` 所以查询执行相同 - 6.0sec ``` SELECT id FROM documents WHERE data->'metadata'->'areaoflawids' ? 'e34e0ee5-78e0-4d92-9186-ac69c109408b'; ``` 执行计划看起来一样:
  • 您能否为此查询显示EXPLAIN (ANALYZE, BUFFERS),以及您要与之比较的其他查询?

标签: arrays postgresql indexing jsonb


【解决方案1】:

在索引中查找 13,614 个候选匹配非常快(1.608 毫秒)。缓慢的部分是从表本身读取所有这些行。如果你打开track_io_timing,然后做EXPLAIN (ANALYZE, BUFFERS),我相信你会发现你正在等待IO。如果连续多次运行查询,它会变得更快吗?

我认为您在这里进行了不相等的基准测试,其中一个表已经在缓存中,而替代表不在。但也可能是新表太大而无法真正放入缓存中。

【讨论】:

    【解决方案2】:

    感谢您的回复!我们从这篇文章中提出了另一个解决方案:https://www.postgresql.org/message-id/CAONrwUFOtnR909gs+7UOdQQB12+pXsGUYu5YHPtbQk5vaE9Gaw@mail.gmail.com。查询现在需要大约 600-800 毫秒来执行。 所以,这里是解决方案:

    CREATE OR REPLACE FUNCTION aol_uuids(data jsonb) RETURNS TEXT[] AS
    $$
        SELECT 
            array_agg(value::TEXT) as val
        FROM 
            jsonb_array_elements(case jsonb_typeof(data) when 'array' then data else '[]' end)
    $$ LANGUAGE SQL IMMUTABLE;
    
    
    SELECT id FROM documents WHERE aol_uuids(data->'metadata'->'areaoflawids')@>ARRAY['"e34e0ee5-78e0-4d92-9186-ac69c109408b"']
    
    

    【讨论】:

    • 因此,您正在将使用本机 UUID 数组的数据模型转换为 JSONB 值(将大小增加 2 以上),只是在需要时将其转换回本机 UUID 数组去寻找它。对我来说听起来很奇怪。
    • 感谢您的评论。嗯,是的,但是这些变化是由需求决定的。我们正在我们的项目中创建新的抽象级别,因此所有特定于实体的字段都在 jsonb 字段内移动。此外,值得注意的是,我们在每个记录的这个数组中存储的项目并不多,而且我们也不受数据库大小的严格限制。所以这个变化对我们来说是合理的。
    猜你喜欢
    • 1970-01-01
    • 2019-06-21
    • 1970-01-01
    • 1970-01-01
    • 2018-08-27
    • 1970-01-01
    • 2019-03-21
    • 2023-02-01
    • 1970-01-01
    相关资源
    最近更新 更多