【问题标题】:Inserting row to temp table将行插入临时表
【发布时间】:2014-11-12 13:33:40
【问题描述】:

我在尝试创建临时表时遇到了性能问题。以下代码是plpgsql函数的一部分:

StartTime := clock_timestamp();
CREATE TEMP TABLE wo_tmp WITH (OIDS)  AS
SELECT workorders1_.woid AS w_id, workorders1_.woid4seg AS w_id4seg
FROM common.workorders workorders1_ 
INNER JOIN common.lines lines2_ ON workorders1_.wolineid=lines2_.lineid
INNER JOIN common.products products2_ ON workorders1_.woprodid=products2_.prodid 
INNER JOIN common.depts depts3_ ON lines2_.linedeptid=depts3_.deptid 
WHERE workorders1_.wostatus='F' 
    AND workorders1_.wotypestatus = ANY ('{R,C,I,D}'::text[])
AND (p_deptid = 0 OR (depts3_.deptid = p_deptid AND ((p_deptid = 5 AND workorders1_.wosegid = 1) OR workorders1_.wosegid = 4)))
AND (p_lineid = 0 OR lines2_.lineid = p_lineid)
AND (p_prodid = 0 OR products2_.prodid = p_prodid)
    AND (p_nrkokili = 0 OR workorders1_.wonrkokili = p_nrkokili)
    AND (p_accepted = TRUE OR workorders1_.worjacceptstatus = 'Y') 
    AND workorders1_.wodateleaverr BETWEEN p_dfr AND p_dto
    AND lines2_.status <> 'D';

CREATE INDEX wo_tmp_w_id_idx
    ON wo_tmp USING btree (w_id ASC NULLS LAST);
CREATE INDEX wo_tmp_w_id4seg_idx
    ON wo_tmp USING btree (w_id4seg ASC NULLS LAST);

    EndTime := clock_timestamp();
    Delta := extract('epoch' from EndTime)::bigint - extract('epoch' from StartTime)::bigint;
    RAISE NOTICE 'Duration [0] in seconds=%', Delta;

这是explain analyze 报告:http://explain.depesz.com/s/uerF
很奇怪,因为当我执行这个函数时,我得到了通知:Duration [0] in seconds=11。我在没有创建临时表的情况下检查查询,结果时间是~300ms

将记录 (~73k) 插入临时表是否可能需要 11 秒?我可以加快速度吗?

【问题讨论】:

  • 索引创建也可能需要一些时间。对过程中的每个查询运行EXPLAIN ANALYZE
  • 我跑了,但只创建临时表需要时间。
  • 始终包含函数页眉和页脚,这是必不可少的细节。另外,pg 8.4 has reached eol and is not supported any more。紧急考虑升级到当前版本。

标签: postgresql plpgsql temp-tables postgresql-8.4


【解决方案1】:

当您在函数中填充临时表时,您会发现不止一个问题:

  • 锁定问题 - 每个临时表都是系统目录中包含某些字段的表。密集创建和删除这些表会产生大量锁定的高开销。有时临时表可以用数组代替。这不是你的情况,因为你需要一个索引。

  • 盲目优化 - PlpgSQL 函数中的嵌入式 SQL 针对大多数常见值进行了优化(此机制在 PostgreSQL 9.2 中略有增强(但仍存在可能的性能问题)。它没有针对当前值进行优化 - 这个事实可以强制执行一些性能问题。然后动态 SQL 是必要的。这个问题的一些链接(onesecond

  • 一些硬件或文件系统问题 - 我对帮助 WITHOUT OIDS 有点困惑。看起来您的文件系统对您来说是一个可怕的瓶颈。临时表存储在文件系统缓存中 - 存储 53K 行应该很快.. 删除 4 个字节(从 35 个字节)并不是太大的变化。

    postgres=# create table t1 with (oids) as select 1 a,2 b,3 c from generate_series(1,73000);
    SELECT 73000
    Time: 302.083 ms
    postgres=# create table t2  as select 1 a,2 b,3 c from generate_series(1,73000);
    SELECT 73000
    Time: 267.459 ms
    postgres=# create temp table t3 with (oids)  as select 1 a,2 b,3 c from generate_series(1,73000);
    SELECT 73000
    Time: 154.431 ms
    postgres=# create temp table t4  as select 1 a,2 b,3 c from generate_series(1,73000);
    SELECT 73000
    Time: 153.085 ms
    postgres=# \dt+ t*
                        List of relations
      Schema   | Name | Type  | Owner |  Size   | Description 
    -----------+------+-------+-------+---------+-------------
     pg_temp_2 | t3   | table | pavel | 3720 kB | 
     pg_temp_2 | t4   | table | pavel | 3160 kB | 
     public    | t1   | table | pavel | 3720 kB | 
     public    | t2   | table | pavel | 3160 kB | 
    (4 rows)
    

将 3MB 文件写入文件系统应该明显少于 1 秒 .. 所以 11 秒的开销很奇怪。 p.s.默认temp_buffers 为 8MB,因此您的结果应仅存储在内存中 - 可能这个假设是错误的 - 更有可能是盲目优化假设。

【讨论】:

  • 谢谢你,很好的解释,我会尝试使用你的提示并让你知道。
  • 还有一件事,我不需要索引,我用它来提高下一个查询的性能,但我可以牺牲大约 1 秒,如果创建临时表将持续 5 秒少。对不起英语
  • 更改为动态查询可以提高我想要的性能。 @Pavel Stehule 做得很好,非常感谢。你是我的英雄。 :)
  • @bemol:如果您实际上不需要索引并且只使用临时表进行下一个查询,则 CTE 而不是临时表可能是更好的解决方案。 Available since pg 8.4. 只是猜测,而您的其余功能未知。
  • 我知道,但我通过这段文字建议自己:Unsupported versions: 8.4
【解决方案2】:

对于初学者,不要将WITH (OIDS) 用于临时表。曾经。不鼓励在常规表中使用 OID。这对于临时表来说是双重的。还减少了磁盘上所需的 RAM / 空间,这可能是这里的主要瓶颈。切换到WITHOUT OIDS

接下来,一个可能的原因(有根据的猜测)是缺少临时缓冲区,这会迫使临时表溢出到磁盘。使用

检查临时表的实际大小
SELECT pg_size_pretty(pg_relation_size('wo_tmp'));

并相应地设置temp_buffers,可能仅用于会话 - 大方地四舍五入,足以避免写入磁盘。

详情:

【讨论】:

  • 谢谢WITHOUT OIDS 加快速度。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-06
  • 2012-07-31
  • 1970-01-01
  • 1970-01-01
  • 2016-07-01
相关资源
最近更新 更多