使用(不正确的?)索引缓慢PostgreSQL查询

时间:2016-04-22 00:13:33

标签: sql database postgresql

我有一个包含3000万行的事件表。以下查询在25秒内返回

SELECT DISTINCT "events"."id", "calendars"."user_id" 
FROM "events" 
LEFT JOIN "calendars" ON "events"."calendar_id" = "calendars"."id" 
WHERE "events"."deleted_at" is null 
AND tstzrange('2016-04-21T12:12:36-07:00', '2016-04-21T12:22:36-07:00') @> lower(time_range) 
AND ("status" is null or (status->>'pre_processed') IS NULL) 

status是一个jsonb列,其索引位于status->>'pre_processed'。以下是在事件表上创建的其他索引。 time_range的类型为TSTZRANGE

CREATE INDEX events_time_range_idx ON events USING gist (time_range);
CREATE INDEX events_lower_time_range_index on events(lower(time_range));
CREATE INDEX events_upper_time_range_index on events(upper(time_range));
CREATE INDEX events_calendar_id_index on events (calendar_id)

我绝对不在我的舒适区域,我正在努力减少查询时间。这是解释分析的输出

  HashAggregate  (cost=7486635.89..7486650.53 rows=1464 width=48) (actual time=26989.272..26989.306 rows=98 loops=1)
  Group Key: events.id, calendars.user_id
  ->  Nested Loop Left Join  (cost=0.42..7486628.57 rows=1464 width=48) (actual time=316.110..26988.941 rows=98 loops=1)
    ->  Seq Scan on events  (cost=0.00..7475629.43 rows=1464 width=50) (actual time=316.049..26985.344 rows=98 loops=1)
          Filter: ((deleted_at IS NULL) AND ((status IS NULL) OR ((status ->> 'pre_processed'::text) IS NULL)) AND ('["2016-04-21 19:12:36+00","2016-04-21 19:22:36+00")'::tstzrange @> lower(time_range)))
          Rows Removed by Filter: 31592898
    ->  Index Scan using calendars_pkey on calendars  (cost=0.42..7.50 rows=1 width=48) (actual time=0.030..0.031 rows=1 loops=98)
          Index Cond: (events.calendar_id = (id)::text)
Planning time: 1.468 ms
Execution time: 26989.370 ms

以下是删除了查询的events.deleted_at部分的解释分析

HashAggregate  (cost=7487382.57..7487398.33 rows=1576 width=48) (actual time=23880.466..23880.503 rows=115 loops=1)
  Group Key: events.id, calendars.user_id
  ->  Nested Loop Left Join  (cost=0.42..7487374.69 rows=1576 width=48) (actual time=16.612..23880.114 rows=115 loops=1)
    ->  Seq Scan on events  (cost=0.00..7475629.43 rows=1576 width=50) (actual time=16.576..23876.844 rows=115 loops=1)
          Filter: (((status IS NULL) OR ((status ->> 'pre_processed'::text) IS NULL)) AND ('["2016-04-21 19:12:36+00","2016-04-21 19:22:36+00")'::tstzrange @> lower(time_range)))
          Rows Removed by Filter: 31592881
    ->  Index Scan using calendars_pkey on calendars  (cost=0.42..7.44 rows=1 width=48) (actual time=0.022..0.023 rows=1 loops=115)
          Index Cond: (events.calendar_id = (id)::text)

计划时间:0.372毫秒 执行时间:23880.571 ms

我在status列上添加了索引。其他所有已经存在的东西,我不确定如何继续前进。有关如何将查询时间缩短到更易管理的数字的任何建议?

2 个答案:

答案 0 :(得分:4)

lower(time_range)上的B树索引只能用于涉及<<==>=和{{1}的条件运营商。 >运算符可能在内部依赖于这些运算符,但就规划器而言,此范围检查操作是一个黑盒子,因此它无法使用索引。

您需要根据B树操作符重新表述您的条件,即:

@>

答案 1 :(得分:0)

因此,为events.deleted_at添加一个索引,以摆脱令人讨厌的顺序扫描。之后会是什么样子?