将行插入临时表

时间:2014-11-12 13:33:40

标签: postgresql plpgsql temp-tables postgresql-8.4

当我尝试创建临时表时,我遇到了性能问题。以下代码是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秒?我可以加快速度吗?

2 个答案:

答案 0 :(得分:2)

当您在函数中填充临时表时,您可以找到多个问题:

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

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

  • 一些hw或文件系统问题 - 我对帮助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秒的开销很奇怪。附:默认temp_buffers是8MB,所以你的结果应该只存储在内存中 - 可能这个假设是假的 - 更可能的是盲优化假设。

答案 1 :(得分:1)

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

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

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

并相应地设置temp_buffers,可能仅用于会话 - 慷慨地整理,足以避免写入磁盘。

详细说明: