0

我有一个问题,即添加一个简单的测试条件(每个查询只应评估一次)导致整个查询需要十倍的时间才能返回。我正在运行 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);

查询必须返回按出价排序的前 20 个匹配错误,其中匹配条件是错误的类型必须符合提供的位掩码(变量 $TYPE),并且与错误关联的标记集必须是提供的超集一组标记名(在准备好的语句中用 $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))

下面是一个测试数据库(带有随机数据)的近似分析结果,其中包含 1,000,000 个错误、2,000 个标签和 bug_tags 中的 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 的结果都 在这里这里

4

1 回答 1

0

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

关于慢查询,有两件事让我印象深刻。

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

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

然而,除了这些以及可能在系统中添加更多内存之外,我没有看到任何让我印象深刻的直接答案。我希望这会有所帮助,如果您在这里无法获得帮助,PostgreSQL 上的 -perform 列表通常非常擅长帮助人们。

于 2013-04-24T07:47:53.717 回答