【问题标题】:Postgres Index scan performing poorlyPostgres 索引扫描表现不佳
【发布时间】:2019-03-19 03:49:36
【问题描述】:

我有一个小表(约 400k 行),该表由 collection_id 索引,并包含一个定义了多个 GIN 索引的 JSON 列,其中一个是值 tagline.id

获取具有特定tagline.id 的所有对象的查询有时非常慢:

explain (analyze, buffers)
SELECT "objects_object"."created",
       "objects_object"."modified",
       "objects_object"."_id",
       "objects_object"."id",
       "objects_object"."collection_id",
       "objects_object"."data",
       "objects_object"."search",
       "objects_object"."location"::bytea
FROM "objects_object"
WHERE ("objects_object"."collection_id" IN (3381, 3321, 3312, 3262, 3068, 2684, 2508, 2159, 2158, 2154, 2157, 2156)
  AND (("objects_object"."data" #>> ARRAY['tagline','id']))::float IN ('8')
  AND ("objects_object"."data" -> 'tagline') ? 'id')
ORDER BY "objects_object"."created" DESC,
         "objects_object"."id" ASC
LIMIT 101;                                                                         


    QUERY PLAN                                                                               
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=8.46..8.47 rows=1 width=1239) (actual time=5513.374..5513.399 rows=101 loops=1)
   Buffers: shared hit=4480 read=6261
   ->  Sort  (cost=8.46..8.47 rows=1 width=1239) (actual time=5513.372..5513.389 rows=101 loops=1)
         Sort Key: created DESC, id
         Sort Method: top-N heapsort  Memory: 247kB
         Buffers: shared hit=4480 read=6261
         ->  Index Scan using index_tagline_id_float_51a27976 on objects_object  (cost=0.42..8.45 rows=1 width=1239) (actual time=943.689..5513.002 rows=235 loops=1)
               Index Cond: (((data #>> '{tagline,id}'::text[]))::double precision = '8'::double precision)
               Filter: (collection_id = ANY ('{3381,3321,3312,3262,3068,2684,2508,2159,2158,2154,2157,2156}'::integer[]))
               Rows Removed by Filter: 47295
               Buffers: shared hit=4480 read=6261
 Planning time: 0.244 ms
 Execution time: 5513.439 ms
(13 rows)

如果执行多次,执行时间会下降到 ~ 5 毫秒。

什么需要这么长时间?为什么在第一次之后执行时间下降那么多?

我认为这与内存无关,因为默认内存 (4MB) 远高于所需内存 (247Kb)。

编辑: 索引定义:

SELECT indexdef FROM pg_indexes 
WHERE indexname = 'index_tagline_id_float_51a27976'; 
                                                                                                 indexdef                                                                                                 
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 CREATE INDEX index_tagline_id_float_51a27976 ON public.objects_object USING btree ((((data #>> ARRAY['tagline'::text, 'id'::text]))::double precision)) WHERE ((data -> 'tagline'::text) ? 'id'::text)
(1 row)

SELECT indexdef FROM pg_indexes 
WHERE indexname = 'objects_object_collection_id_6f1559f5'; 
                                                indexdef                                                 
---------------------------------------------------------------------------------------------------------
 CREATE INDEX objects_object_collection_id_6f1559f5 ON public.objects_object USING btree (collection_id)
(1 row)

编辑:

添加索引后test:

select indexdef from pg_indexes where indexname='test'; 
                                                                                          indexdef                                                                                          
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 CREATE INDEX test ON public.objects_object USING btree ((((data #>> ARRAY['tagline'::text, 'id'::text]))::double precision), collection_id) WHERE ((data -> 'tagline'::text) ? 'id'::text)
(1 row)

执行时间减少了,但是缓冲区共享命中,不确定这是否提高了性能:

                                                                 QUERY PLAN                                                                 
--------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=8.46..8.47 rows=1 width=1238) (actual time=1721.260..1721.281 rows=101 loops=1)
   Buffers: shared hit=5460 read=5115
   ->  Sort  (cost=8.46..8.47 rows=1 width=1238) (actual time=1721.257..1721.270 rows=101 loops=1)
         Sort Key: created DESC, id
         Sort Method: top-N heapsort  Memory: 298kB
         Buffers: shared hit=5460 read=5115
         ->  Index Scan using test on objects_object  (cost=0.42..8.45 rows=1 width=1238) (actual time=1682.637..1720.793 rows=235 loops=1)
               Index Cond: (((data #>> '{tagline,id}'::text[]))::double precision = '8'::double precision)
               Filter: (collection_id = ANY ('{3381,3321,3312,3262,3068,2684,2508,2159,2158,2154,2157,2156}'::integer[]))
               Rows Removed by Filter: 47295
               Buffers: shared hit=5454 read=5115
 Planning time: 238.364 ms
 Execution time: 1762.996 ms
(13 rows)

问题似乎是collection_id应该是索引条件的一部分,而不是过滤,这样可以避免从(慢)数据存储中获取大量数据。

为什么索引没有按预期工作?

更新: 显然影响查询计划的参数顺序, 我将索引重写为:

select indexdef from pg_indexes where indexname='test'; 
                                                                                          indexdef                                                                                          
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 CREATE INDEX test ON public.objects_object USING btree (collection_id, (((data #>> ARRAY['tagline'::text, 'id'::text]))::double precision)) WHERE ((data -> 'tagline'::text) ? 'id'::text)

现在运行查询我们可以看到读取记录的数量较少:

 Limit  (cost=57.15..57.16 rows=1 width=1177) (actual time=1.043..1.059 rows=101 loops=1)
   Buffers: shared hit=101 read=10
   ->  Sort  (cost=57.15..57.16 rows=1 width=1177) (actual time=1.040..1.047 rows=101 loops=1)
         Sort Key: created DESC, id
         Sort Method: top-N heapsort  Memory: 304kB
         Buffers: shared hit=101 read=10
         ->  Index Scan using test on objects_object  (cost=0.42..57.14 rows=1 width=1177) (actual time=0.094..0.670 rows=232 loops=1)
               Index Cond: ((collection_id = ANY ('{3381,3321,3312,3262,3068,2684,2508,2159,2158,2154,2157,2156}'::integer[])) AND (((data #>> '{tagline,id}'::text[]))::double precision = '8'::double precisio
n))
               Buffers: shared hit=95 read=10
 Planning time: 416.365 ms
 Execution time: 43.463 ms
(11 rows)

【问题讨论】:

  • 现有(部分?)索引的定义是什么?你有collection_id 的索引吗?
  • 是的,collection_id 上有一个索引,但据我所知,它没有用于此查询。
  • 桌面上的ANALYZE 能提高性能吗?
  • 不是真的,改进不大,但仍然很慢。
  • 请将索引定义添加到问题中。 last_analyze 为 NULL 是正常的(但 last_autoanalyze 不应该是)。

标签: postgresql indexing


【解决方案1】:

可以使用以下索引加速这个特定的查询:

CREATE INDEX ON public.objects_object (
   ((data #>> ARRAY['tagline'::text, 'id'::text])::double precision),
   collection_id
) WHERE (data -> 'tagline') ? 'id';

这将避免索引扫描中的过滤器,这是花费大部分时间的地方。

【讨论】:

  • 有一点我无法理解,为什么 EXPLAIN ANALYZE .. 在重复运行时返回不同的执行时间?
  • 因为缓存了更多数据。请参阅执行计划中的Buffers: shared hit=4480 read=6261 行。
  • 其实我太乐观了,执行时间还是挺长的(见编辑)。
  • 莫名其妙为什么不用索引而不是过滤条件。你在创建额外索引后运行了ANALYZE,对吧?
  • 我运行了分析,但似乎在索引和查询中指定字段的顺序很重要,请检查我放置的更新。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-23
  • 2020-02-14
  • 2023-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多