Postgresql函数卡在for循环中

时间:2015-05-18 08:22:45

标签: postgresql loops for-loop locking

该功能在循环之前被卡住。

RewriteCond %{HTTP_HOST} !^(www|sub)\. [NC]
RewriteRule ^ http://www.%{HTTP_HOST}/$1 [R=301,L,NE]

打印消息“开始...”,但不会打印消息“...结束”。

select * from scm_main.fn_connection_stations(1219646)

查询本身非常简单,并在直接调用时运行:

CREATE OR REPLACE FUNCTION     
scm_main.fn_connection_stations(var_connection_id bigint)
RETURNS SETOF scm_main.typ_connection_stations AS
$BODY$ DECLARE 
    var_affected                 INTEGER DEFAULT 0; 
    var_row                      scm_main.typ_connection_stations%ROWTYPE; 
BEGIN 
    RAISE NOTICE 'Start...'; 
    FOR var_row IN 
    SELECT DISTINCT v.vbvdata_station_id
    FROM scm_main.tbl_vbvdata AS v 
    INNER JOIN scm_main.tbl_packet AS p ON (v.vbvdata_packet_id = 
    p.packet_id and p.packet_connection_id = var_connection_id) 
    --WHERE v.vbvdata_packet_id IN 
    --( SELECT packet_id FROM scm_main.tbl_packet AS o_p WHERE 
    o_p.packet_connection_id = var_connection_id) 
    LOOP 
        RETURN NEXT var_row; 
    END LOOP; 
    RAISE NOTICE '...End'; 
    RETURN; 
END 
$BODY$
  LANGUAGE plpgsql STABLE STRICT
  COST 100
  ROWS 1000;
ALTER FUNCTION scm_main.fn_connection_stations(bigint)
  OWNER TO postgres;

我猜问题必须是表格的锁定。但我绝对不明白是什么原因以及可以解决的问题。

前段时间,评论部分正在运作。但过了一会儿,出现了同样的问题。我通过更改查询并用内部联接替换条件(级联查询)来解决它。但这次他们都没有工作!

更新

我通过一次愚蠢的改变再次成功:

SELECT DISTINCT v.vbvdata_station_id 
FROM scm_main.tbl_vbvdata AS v 
INNER JOIN scm_main.tbl_packet AS p ON (v.vbvdata_packet_id =  
p.packet_id and p.packet_connection_id = 1219646) 

更改为:

INNER JOIN scm_main.tbl_packet AS p ON (v.vbvdata_packet_id = p.packet_id 
  and p.packet_connection_id = var_connection_id)`

并且工作得很好。

另一个有趣的观点是,即使是以下更改也无法正常工作,但仍然存在:

INNER JOIN scm_main.tbl_packet AS p ON (v.vbvdata_packet_id = p.packet_id)
  where p.packet_connection_id in (select var_connection_id)

现在我找到了解决方案。但我想知道它是怎么发生的!

1 个答案:

答案 0 :(得分:1)

如果通过对查询进行如此肤浅的更改来修复它,那么它可能不会被卡住"但由于选择不当的执行计划而只是非常缓慢地运行。

查询在函数中的性能比它本身更差的事实可能是plan caching的结果。基本上,Postgres可能会尝试通过创建和重用通用(即参数独立)执行计划来避免重新规划查询的成本。不幸的是,这些通用计划可能远非最佳。

糟糕计划的可能解释是糟糕的统计数据;如果Postgres没有关于数据的准确信息,它必然会做出糟糕的决定。所涉及的ANALYSE表可能有所帮助,但通常不需要这样做 - autovacuum通常应该使统计数据保持最新(假设你已经运行)。

统计偏差的一个常见原因是值的分布不均匀(在您的情况下,如果每个连接的数据包数量有很大差异)。通过使用ALTER TABLE ... SET STATISTICS(后跟ANALYSE)提高统计信息的详细程度,可以大大改善这一点。在某些情况下,高值会减慢计划,但500(可能)是一个安全的起点。

如果没有其他方法可行,您可以通过EXECUTE运行查询来绕过计划缓存。