由于我不知道值的分布,因此没有明确的方法来解决问题。
但是一个问题是显而易见的:eventtime 列有一个索引,但是由于该查询使用该列上的函数进行操作,因此无法使用该索引。
eventtime in time zone 'UTC' > CURRENT_DATE
要么必须删除索引并使用该函数重新创建索引,要么必须重写查询。
重新创建索引(示例):
CREATE INDEX ON t_el_eventlog (timezone('UTC'::text, eventtime));
(这与 相同eventtime in time zone 'UTC'
)
这样过滤器与函数匹配,索引就可以使用了。
我怀疑 sourceid 没有很好的分布,没有非常不同的值。在这种情况下,删除 sourceid 上的索引并删除 eventtime 上的索引并在 eventtime 和 sourceid 上创建一个新索引可能是一个想法:
CREATE INDEX ON t_el_eventlog (timezone('UTC'::text, eventtime), sourceid);
这就是理论告诉我们的。我对此进行了一些测试,一张表有大约 1000 万行,事件时间分布在 36 小时内,只有 20 个不同的 sourceids (1..20)。分布非常随机。最好的结果是在 eventtime、sourceid(无函数索引)和调整查询的索引中。
CREATE INDEX ON t_el_eventlog (eventtime, sourceid);
-- make sure there is no index on source id. we need to force postgres to this index.
-- make sure, postgres learns about our index
ANALYZE; VACUUM;
-- use timezone function on current date (guessing timezone is CET)
SELECT * FROM t_el_eventlog
WHERE eventtime > timezone('CET',CURRENT_DATE) AND sourceid = 14;
对于有 10'000'000 行的表,此查询仅在 400 毫秒内返回大约 500'000 行。(而不是在所有其他组合中大约 1400 到 1700)。
寻找索引和查询之间的最佳匹配是任务。我建议进行一些研究,建议是http://use-the-index-luke.com
这是最后一种方法的查询计划的样子:
Index Only Scan using evlog_eventtime_sourceid_idx on evlog (cost=0.45..218195.13 rows=424534 width=0)
Index Cond: ((eventtime > timezone('CET'::text, (('now'::cstring)::date)::timestamp with time zone)) AND (sourceid = 14))
正如你所看到的,这是一个完美的匹配......