【问题标题】:Adding a GIN index on a JSONB column slows down my request在 JSONB 列上添加 GIN 索引会减慢我的请求
【发布时间】:2020-02-17 19:02:35
【问题描述】:

我来自 Mysql 和 nosql 数据库,我是 Postgress db 的新手。我在 Aurora AWS 上使用 Postgres 11.6。

我正在尝试创建由两列组成的表,一个键和一个 jsonb 值。

每个值如下所示:

{"game": "game6",
   "username": "Djobi",   
   (bunch of fields)
   "permissions": ["permission3", "permission1", "permission5"]}

我正在尝试添加不同的索引以查看数据库的容量。其中之一是按用户名查找用户。另一种是寻找具有特定权限的用户。

 account_index   | account_index_username    | CREATE INDEX account_index_username ON public.account_index USING btree (((value -> 'username'::text)))
 account_index   | account_index_permissions | CREATE INDEX account_index_permissions ON public.account_index USING gin (((value -> 'permissions'::text)))
 account_index   | account_global_gin        | CREATE INDEX accountgin ON public.account_index USING gin (value)

我有两张表,数据完全相同。每个表大约有 3000 万行。一个有索引,一个没有。我正在运行以下查询来测试性能:

SELECT 1 
FROM account_noindex 
WHERE value @> '{"permissions": ["permission1"]}' limit 10;

我正在这里寻找有权限的用户1。 (旁注,尚不确定如何询问如何获得特别许可 1 与包括许可 1 在内的任何许可) 在我的索引表上运行我的请求时,我得到了 5000 毫秒的响应时间。 在我的非索引表上运行我的请求时,我得到了 50 ms 的响应时间。

所以非索引表比索引表快 100 倍,我不得不说这让我很困惑。 如果我尝试对这两个查询进行解释,我会得到以下结果:

索引表:

Limit  (cost=430.49..468.31 rows=10 width=32)
   ->  Bitmap Heap Scan on account_index  (cost=430.49..144146.44 rows=37999 width=32)
         Recheck Cond: (value @> '{"permissions": ["permission1"]}'::jsonb)
         ->  Bitmap Index Scan on accountgin  (cost=0.00..420.99 rows=37999 width=0)
               Index Cond: (value @> '{"permissions": ["permission1"]}'::jsonb)
(5 rows)

非索引表

Limit  (cost=0.00..1935.23 rows=10 width=4)
   ->  Seq Scan on account_noindex  (cost=0.00..7360637.42 rows=38035 width=4)
         Filter: (value @> '{"permissions": ["permission1"]}'::jsonb)

如果我尝试更深入地偏移(超过 100k)差异不太明显,但非索引表仍然更快。

[编辑] 这里是索引表的完整分析缓冲区:

EXPLAIN (ANALYZE, BUFFERS) SELECT value->'permissions' FROM account_index WHERE value @> '{"permissions": ["permission1"]}' limit 12;
                                                                QUERY PLAN                                                                 
-------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=430.49..475.88 rows=12 width=32) (actual time=1926.289..1926.365 rows=12 loops=1)
   Buffers: shared hit=24398
   ->  Bitmap Heap Scan on account_index  (cost=430.49..144146.44 rows=37999 width=32) (actual time=1926.287..1926.361 rows=12 loops=1)
         Recheck Cond: (value @> '{"permissions": ["permission1"]}'::jsonb)
         Rows Removed by Index Recheck: 30
         Heap Blocks: lossy=8
         Buffers: shared hit=24398
         ->  Bitmap Index Scan on accountgin  (cost=0.00..420.99 rows=37999 width=0) (actual time=1916.655..1916.656 rows=8386144 loops=1)
               Index Cond: (value @> '{"permissions": ["permission1"]}'::jsonb)
               Buffers: shared hit=24390
 Planning Time: 0.073 ms
 Execution Time: 1927.143 ms

那么我在这里缺少什么?我是否错误地创建了 GIN 索引?还是我做错了我的要求?

请注意,我的用户名索引也有同样的问题。当请求查找具有特定用户名且没有任何限制的用户时,我会得到一个带或不带 GIN 索引的全表扫描。当我添加 BTREE 索引时,没有问题,不需要限制。

【问题讨论】:

    标签: postgresql indexing jsonb gwt-gin


    【解决方案1】:

    JSONB 列没有收集到有用的统计信息。该数据库必须对它将找到多少符合@>的行做出通用假设,而这些通常是错误的。在决定执行 LIMIT 查询的绝对最佳方法时,拥有更准确的统计数据非常重要。一般假设是 @> 将匹配表的 0.1%。如果这对你的情况来说是严重错误的,你可能会得到糟糕的计划。

    尽管计划之间的差异确实看起来很极端。如果没有看到EXPLAIN (ANALYZE, BUFFERS),很难说任何具体的内容。

    请注意,我的用户名索引也有同样的问题。如果我只有 GIN 索引,我会得到非常奇怪的结果。当我添加 BTREE 索引时就没有问题了。

    我不知道“奇怪”的结果可能是什么。您必须提供示例。

    【讨论】:

    • 谢谢,我编辑了原始帖子以解释其他奇怪之处。我想原因与您解释的类似。我还添加了分析。
    猜你喜欢
    • 2019-01-29
    • 1970-01-01
    • 1970-01-01
    • 2021-02-22
    • 1970-01-01
    • 2016-03-06
    • 2020-06-10
    • 2015-09-02
    • 1970-01-01
    相关资源
    最近更新 更多