让我直说吧。
你有一个表posts
。你有一个表posts_types
。这两人在posts_types_assignment
上有多次加入。你有这样的一些查询是慢的:
SELECT count(*)
FROM posts p
JOIN posts_types_assigment pta1
ON p.id = pta1.post_id
JOIN posts_types pt1
ON pt1.id = pta1.post_type_id
AND pt1.type = 'language'
AND pt1.name = 'English'
JOIN posts_types_assigment pta2
ON p.id = pta2.post_id
JOIN posts_types pt2
ON pt2.id = pta2.post_type_id
AND pt2.type = 'tag'
AND pt2.name = 'awesome'
而你想知道为什么它是痛苦的缓慢。
我的第一个注意事项是,如果您在posts
表中有标识符而不是在连接中,PostgreSQL将不得不做很少的工作。但这是一个没有实际意义的问题,已经做出了决定。
我更有用的说明是我相信PostgreSQL有一个类似于Oracle的查询优化器。在这种情况下,为了限制必须考虑的可能查询计划的组合爆炸,它只考虑以某个表开始的计划,然后一次重复连接另一个数据集。然而,没有这样的查询计划在这里工作。你可以从pt1
开始,得到1条记录,然后去pta1
,拿到一堆记录,加入p
,用相同数量的记录结束,然后加入pta2
,现在你得到了大量的记录,然后加入pt2
,只有几个记录。加入pta2
是一个缓慢的步骤,因为数据库不知道您想要哪个记录,因此必须为每个帖子和一条元数据(类型,语言或标记)组合创建一个临时结果集。
如果这确实是您的问题,那么正确的计划看起来像这样。加入pt1
至pta1
,在其上放置索引。加入pt2
至pta2
,然后加入到第一个查询的结果中,然后加入到p
。然后算。这意味着我们不会得到巨大的结果集。
如果出现这种情况,没有办法告诉查询优化器,一旦您想要它想出新的执行计划类型。但是有办法强制它。
CREATE TEMPORARY TABLE t1
AS
SELECT pta*
FROM posts_types pt
JOIN posts_types_assignment pta
ON pt.id = pta.post_type_id
WHERE pt.type = 'language'
AND pt.name = 'English';
CREATE INDEX idx1 ON t1 (post_id);
CREATE TEMPORARY TABLE t2
AS
SELECT pta*
FROM posts_types pt
JOIN posts_types_assignment pta
ON pt.id = pta.post_type_id
JOIN t1
ON t1.post_id = pta.post_id
WHERE pt.type = 'language'
AND pt.name = 'English';
SELECT COUNT(*)
FROM posts p
JOIN t1
ON p.id = t1.post_id;
除了随机错字等,这可能会表现得更好。如果没有,请仔细检查表格上的索引。
你做了一个[解释](http://www.postgresql.org/docs/9.0/static/sql-explain.html)来查看查询计划吗? – 2011-04-11 19:48:10
@Sam是的,我做了,我猜(根据我在许多地方发现的信息),这是Postgre本身的问题。现在我试图“选择COUNT(*)FROM帖子”,其中〜1 500 000条记录和COUNTing记录约9秒。第二次(Postgre可能会缓存查询,或者其他什么)花费约2秒。无论如何,我认为2秒甚至是非常慢。 – 2011-04-12 09:21:57
您是否尝试过创建索引?不要忘记部分索引。如果您只需要索引数据的一部分,它们可以产生巨大的性能差异。如果您已经完成了这些显而易见的事情,但仍然无法改善,我建议您发布更多详细信息以获得更多帮助(表格架构,示例数据,示例查询,解释计划,postgresql版本等)。 – 2011-04-12 17:42:05