在JSONB列上添加GIN索引会降低我的请求速度



我来自Mysql和nosql数据库,我是Postgress数据库的新手。我在Aurora AWS上使用Postgres11.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在内的任何权限(在索引表上运行请求时,我得到了5000ms的响应时间。在非索引表上运行请求时,我得到50毫秒的响应时间。

所以非索引表比索引表快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索引?还是我的请求做错了

请注意,我的用户名上的索引也有同样的问题。当请求查找具有不带任何LIMIT的特定用户名的用户时,无论是否使用GIN索引,我都会得到一个完整的表扫描。当我添加BTREE指数时,没有问题,也不需要限制。

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

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

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

我不知道"奇怪"的结果可能是什么。你必须提供例子。

相关内容

  • 没有找到相关文章

最新更新