【问题标题】:PostgreSQL using different index for same queryPostgreSQL 对同一查询使用不同的索引
【发布时间】:2018-11-22 07:06:30
【问题描述】:

我有一个 SQL 查询,它在两个表上使用内部联接并根据几个参数过滤数据。按照查询计划,对于不同的查询参数值(如不同的日期范围),Postgres 使用不同的索引。

我知道 Postgres 会根据结果集中的行数来确定是否必须使用索引。但是为什么 Postgres 选择对相同的查询使用不同的索引。在这两种情况下,查询时间相差 10 倍。如何优化查询?由于 Postgres 不允许用户定义要在查询中使用的索引。

编辑:

explain (analyze, buffers, verbose) SELECT COUNT(*) FROM "bookings" INNER JOIN "hotels" ON "hotels"."id" = "bookings"."hotel_id" WHERE "bookings"."hotel_id" = 37016 AND (bookings.status in (0,1,2,3,4,5,6,7,9,10,11,12)) AND (bookings.source in (0,1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70) or bookings.status in (0,1,2,3,4,5,6,7,8,9,10,11,13)) AND (
bookings.source in (4,66,65)
OR
date(timezone('+05:30',bookings.created_at))>checkin
OR
(
( date(timezone('+05:30',bookings.created_at))=checkin
and
extract (epoch from COALESCE(cancellation_time,NOW())-bookings.created_at)>600
)
OR
( date(timezone('+05:30',bookings.created_at))<checkin
and
extract (epoch from COALESCE(cancellation_time,NOW())-bookings.created_at)>600
and
(
extract (epoch from ((bookings.checkin||' '||hotels.checkin_time)::timestamp -COALESCE(cancellation_time,bookings.checkin))) < extract(epoch from '16 hours'::interval)
OR
(DATE(bookings.checkout)-DATE(bookings.checkin))*(COALESCE(bookings.oyo_rooms,0)+COALESCE(bookings.owner_rooms,0)) > 3
)
)
)
) AND (bookings.checkin >= '2018-11-21') AND (bookings.checkin <= '2019-05-19') AND "bookings"."hotel_id" = '37016' AND "bookings"."status" IN (0, 1, 2, 3, 12);

查询计划:https://explain.depesz.com/s/SPeb

explain (analyze, buffers, verbose) SELECT COUNT(*) FROM "bookings" INNER JOIN "hotels" ON "hotels"."id" = 37016 WHERE "bookings"."hotel_id" = 37016 AND (bookings.status in (0,1,2,3,4,5,6,7,9,10,11,12)) AND (bookings.source in (0,1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70) or bookings.status in (0,1,2,3,4,5,6,7,8,9,10,11,13)) AND (
bookings.source in (4,66,65)
OR
date(timezone('+05:30',bookings.created_at))>checkin
OR
(
( date(timezone('+05:30',bookings.created_at))=checkin
and
extract (epoch from COALESCE(cancellation_time,now())-bookings.created_at)>600
)
OR
( date(timezone('+05:30',bookings.created_at))<checkin
and
extract (epoch from COALESCE(cancellation_time,now())-bookings.created_at)>600
and
(extract (epoch from ((bookings.checkin||' '||hotels.checkin_time)::timestamp -COALESCE(cancellation_time,bookings.checkin))) < extract(epoch from '16 hours'::interval)
OR
(DATE(bookings.checkout)-DATE(bookings.checkin))*(COALESCE(bookings.oyo_rooms,0)+COALESCE(bookings.owner_rooms,0)) > 3
)
)
)
) AND (bookings.checkin >= '2018-11-22') AND (bookings.checkin <= '2019-05-19') AND "bookings"."hotel_id" = '37016' AND "bookings"."status" IN (0,1,2,3,4,12);

查询计划:https://explain.depesz.com/s/DWD

【问题讨论】:

  • @a_horse_with_no_name ,编辑了帖子。
  • 不相关,但是:AND "bookings"."hotel_id" = '37016' AND "bookings"."status" IN (0, 1, 2, 3, 12)结尾的两个条件没有用,因为开头有WHERE "bookings"."hotel_id" = 37016,还有更大的状态范围:AND (bookings.status IN (0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12))
  • 似乎随着日期范围的扩大,优化器期望更多的行,因此使用不同的索引。运行analyze bookings(或vacuum analyze bookings)会改变什么吗?
  • 不同的IN列表也可能会产生影响,

标签: postgresql indexing query-optimization


【解决方案1】:

终于找到了解决这个问题的办法。我根据列的 10 多个可能值进行查询(在本例中为 status)。如果我将此查询分解为多个子查询,每个查询仅针对 1 个状态值并使用 union all 聚合结果,则执行的查询计划对每个子查询使用优化索引。

结果:此次更改后查询时间减少了 10 倍。

对此行为的可能解释是,查询计划器为每个子查询获取较少的行数,并在这种情况下使用优化的索引。我不确定这是否是正确的解释。

【讨论】:

    猜你喜欢
    • 2010-11-06
    • 2012-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多