应该评估一次的条件会显着降低性能

时间:2013-02-07 15:40:34

标签: sql postgresql

我有一个问题,即添加一个简单的测试条件(应该是 每个查询只评估一次)导致整个查询占用十次 只要回来。我正在运行PostgreSQL 9.2,并且正在运行的表格 如下:

CREATE TABLE tags (
    tid     int4 UNIQUE NOT NULL,
    name    text UNIQUE NOT NULL,
    PRIMARY KEY (tid));

CREATE TABLE bugs (
    bid     int4 UNIQUE NOT NULL,
    type    int2 NOT NULL,
    title   text NOT NULL,
    PRIMARY KEY (bid));

CREATE TABLE bug_tags (
    bid     int4 REFERENCES bugs(bid) NOT NULL,
    tid     int4 REFERENCES tags(tid) NOT NULL,
    PRIMARY KEY (bid, tid));

CREATE INDEX bug_tags_bid_idx ON bug_tags (bid);
CREATE INDEX bug_tags_tid_idx ON bug_tags (tid);

查询必须返回按bid排序的前20个匹配错误,其中 匹配条件是bug的类型必须符合提供的 位掩码(变量$ TYPE),以及与bug关联的标记集必须 是一组提供的标记名的超集(在$中用$ TAGNAMES表示) 准备好的声明)。在尝试了几种方法之后,下面是一种方法 这会产生最好的结果。基本的想法是首先获取所有 错误满足$ TAGNAMES条件,然后测试每一个 $ TYPE条件。此外,如果$ TAGNAMES为空,那么我们跳过提取 那些错误,而是专注于循环整个表 (因为我们只想要前20行,所以这应该快得多)。 后一种测试导致了令人费解的结果。

WITH tids AS
            (SELECT tid
            FROM    tags
            WHERE   name = ANY ($TAGNAMES :: text[])),
    bugs_from_tags AS
            (SELECT DISTINCT t1.bid
            FROM    bug_tags AS t1
            WHERE   NOT EXISTS
                    (SELECT *
                    FROM    tids
                    WHERE   NOT EXISTS
                            (SELECT *
                            FROM    bug_tags AS t2
                            WHERE   t2.tid = tids.tid AND t2.bid = t1.bid)))
SELECT bid, type, title
FROM bugs
WHERE (type & $TYPE <> 0) AND
  (($TAGNAMES :: text[]) = '{}' OR bid IN (SELECT * FROM bugs_from_tags))
ORDER BY bid
LIMIT 20;

没有测试的版本当然是相同的,除了WHERE子句, 这看起来像这样:

WHERE (type & $TYPE <> 0) AND (bid IN (SELECT * FROM bugs_from_tags))

以下是测试数据库的近似ANALYZE结果(随机 数据)由bug_tags中的1,000,000个错误,2,000个标记和200,000行组成。 “with”和“without”列指的是带/不带查询的版本 对空的测试。数字以毫秒为单位。

                            with    without
len($TAGNAMES) = 0:         4       1500
len($TAGNAMES) = 1:         13000   1600
显然,当len($ TAGNAMES)=时,添加测试条件会得到很好的回报 0,否则会导致灾难。这令人费解,因为条件 独立于每一行,因此只应评估一次。 此外,EXPLAIN ANALYZE(很长)的结果确实显示出来 当len($ TAGNAMES)= 1时,有或没有使用的计划是 非常不同,虽然它们应该完全相同!

无论如何,我怀疑我偶然发现了PostgreSQL查询的怪癖(或错误?) 规划师。关于如何绕过它的任何想法?

注意:两个测试的EXPLAIN ANALYZE结果都是 herehere

1 个答案:

答案 0 :(得分:0)

阅读计划,主要区别在于快速首先过滤结果然后加入它们。第二个过滤器和嵌套循环连接。

有关缓慢查询的两件事让我感到震惊。

首先是它选择了一个糟糕的计划。当您没有足够的内存以确保其他计划更好时,就会发生这种情况。尝试将effective_cache_size设置得更高一点,将work_mem设置得更高一些。

第二个问题是慢速查询缺少哈希聚合。我建议将DISTINCT更改为GROUP BY,看看这是否有帮助。我的猜测是,在缓慢的情况下,它可能会实现CTE,然后将查询作为嵌套循环运行。

然而,除了这些以及可能在系统中放入更多内存之外,我没有看到任何让我感到紧张的答案。我希望这有用,如果你不能在这里得到帮助,PostgreSQL上的-perform列表通常非常善于帮助人们。