我正在使用Postgres 9.1并且查询性能非常低。
Explain Analyze SELECT COUNT(DISTINCT email) FROM "invites" WHERE (
created_at < '2012-10-10 21:08:05.259200'
AND invite_method = 'email'
AND accept_count = 0
AND reminded_count < 3
AND (last_reminded_at IS NULL OR last_reminded_at < '2012-10-10 21:08:05.261483'))
Aggregate (cost=19828.24..19828.25 rows=1 width=21) (actual time=11395.903..11395.903 rows=1 loops=1)
-> Seq Scan on invites (cost=0.00..18970.57 rows=343068 width=21) (actual time=0.036..353.121 rows=337143 loops=1)
Filter: ((created_at < '2012-10-10 21:08:05.2592'::timestamp without time zone) AND (reminded_count < 3) AND ((last_reminded_at IS NULL) OR (last_reminded_at < '2012-10-10 21:08:05.261483'::timestamp without time zone)) AND ((invite_method)::text = 'email'::text) AND (accept_count = 0))
Total runtime: 11395.970 ms
正如您所看到的,这需要大约11秒钟。我如何添加索引来优化此查询性能?
答案 0 :(得分:6)
只需将@Jim建议的“一切”编入索引并不是一个非常有效的策略。索引确实需要维护和组合许多单个索引的成本比一个定制的索引更昂贵(维护和查询)。它总是取决于你的完整情况。
对于只读或很少写入的表,索引的成本很低,但对于具有大量写入操作的易失性表而言,索引的成本很高。另一个缺点是索引禁止HOT更新(仅堆元组)。 More in this related answer.
如果特定查询的效果很重要,partial multi-column index将是一个很好的策略。专业,但比所有相关列上的单个索引更便宜,更快。经验法则是......
WHERE
子句中的稳定条件(每个查询都相同)来缩小索引的分区。根据您的列名称(缺少信息),accept_count = 0
似乎是最具选择性(和稳定)的过滤器,而created_at
和last_reminded_at
可能会不断变化。也许是这样的:
CREATE INDEX invites_special_idx
ON invites (created_at, last_reminded_at)
WHERE accept_count = 0
AND invite_method = 'email'
AND reminded_count < 3;
排序created_at
和last_reminded_at
升序以完美匹配查询 - 这恰好是默认设置。这样,系统可以从索引顶部的单次扫描中获取所有相关行。应该非常快。
正如我们在您之前的一个问题中所讨论的那样,在索引上对表进行聚类可能会有额外的帮助。请务必阅读有关CLUSTER
的手册
正如@Craig所提供的,您不能CLUSTER
部分索引。由于CLUSTER
是一次性操作(后期写入操作会降低效果),您可以通过创建完整索引来避免此限制,CLUSTER
表再次删除索引。像:
CREATE INDEX invites_special_idx2 ON invites (created_at, last_reminded_at);
CLUSTER invites USING invites_special_idx2;
DROP INDEX invites_special_idx2;
CLUSTER
只有在没有其他重要查询对数据分布有矛盾的要求时才有用。
PostgreSQL 9.2有一些新功能可能会使您的查询更快。特别是仅索引扫描(first item in the release notes)。您可能需要考虑升级。
答案 1 :(得分:0)
您应该为email,created_at,invite_method,accept_count,reminded_count和last_reminded添加索引。通常,WHERE语句左侧的任何内容。