2011-04-11 158 views
1

我有个职位,其中由分类表:PostgreSQL缓慢COUNT() - 是否触发唯一的解决方案?

  • 标签
  • 语言

所有这些 “类” 被存储在接下来的表(posts_types)和通过下表连接(posts_types_assignment)。

COUNTing in PostgreSQL is really slow(我在该表中有超过500k条记录),我需要获取按类型/ tag/lang的任意组合分类的帖子数量。

如果我通过触发器来解决它,它会充满许多多级循环,这看起来确实看起来不错,而且很难维护。

是否有任何其他解决方案如何有效地获得任何类型/标签/语言分类的帖子的实际数量?

+0

你做了一个[解释](http://www.postgresql.org/docs/9.0/static/sql-explain.html)来查看查询计划吗? – 2011-04-11 19:48:10

+0

@Sam是的,我做了,我猜(根据我在许多地方发现的信息),这是Postgre本身的问题。现在我试图“选择COUNT(*)FROM帖子”,其中〜1 500 000条记录和COUNTing记录约9秒。第二次(Postgre可能会缓存查询,或者其他什么)花费约2秒。无论如何,我认为2秒甚至是非常慢。 – 2011-04-12 09:21:57

+1

您是否尝试过创建索引?不要忘记部分索引。如果您只需要索引数据的一部分,它们可以产生巨大的性能差异。如果您已经完成了这些显而易见的事情,但仍然无法改善,我建议您发布更多详细信息以获得更多帮助(表格架构,示例数据,示例查询,解释计划,postgresql版本等)。 – 2011-04-12 17:42:05

回答

1

让我直说吧。

你有一个表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是一个缓慢的步骤,因为数据库不知道您想要哪个记录,因此必须为每个帖子和一条元数据(类型,语言或标记)组合创建一个临时结果集。

如果这确实是您的问题,那么正确的计划看起来像这样。加入pt1pta1,在其上放置索引。加入pt2pta2,然后加入到第一个查询的结果中,然后加入到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; 

除了随机错字等,这可能会表现得更好。如果没有,请仔细检查表格上的索引。

1

由于btilly笔记,如果他猜中的模式,表的设计并不能帮助 - 它似乎(一见钟情,至少),例如,有三个表posts_tag(post_id,tag)post_lang(post_id,lang)post_type(post_id,type)会更自然并且效率更高。

除此之外(或除此之外),可以考虑总结所有可能计数的表格或物化视图,列号为(lang,type,tag,nposts)。当然,要全面计算这个值会非常慢,但是(除了第一次)可以完全“在后台”,在某个时间间隔内完成(如果数据变化不大,吨需要确切的计数),或者急切地使用触发器。 举例看看here

相关问题