多次运行后,plpgsql函数中的Postgresql查询速度变慢

时间:2015-12-10 22:24:29

标签: postgresql

我在plpgsql函数中有一个查询(见下文,为了清楚起见而简化),在4或5次运行后减速约5倍(运行时间从~700 ms到~3.5秒)。如果我删除并重新创建该功能,它将重新开始:快速约5次然后慢。所以,似乎查询优化器有一些问题,但我无法获得查询计划,因为它在函数中。如果我在具有相同输入的函数外部运行查询,则无论运行多少次都可以正常运行。如果我注释掉3个左连接,保持它在函数内部,再次运行正常。所以,我得出的结论是它与左连接有关,但那是关于它的。

有关查询的更多信息:在返回with as约5000行和40行后,它有一个data子句;然后,它会自行连接3次以将一些信息从行移动到列(这会将行减少到小于1000)。

create or replace function public.a_function(
-- 5 input parameters here
)

  returns table (a text, b integer, ...) -- about 40 output columns

as $$

declare
...
begin

... --logic to prepare two arrays used in query below: array_a and array_b

   return query
    with data as (                          -- this part of the query returns ~5000 rows and 40 columns
    select d.*
    from public.dashboard_data_view d       -- ~10 million rows in this view
    where d.organization_id = any (array_a) -- array_a: 4 values in int[]
    and d.element_id = any (array_b)        -- array_b: ~10,000 values in int[]
      )
    select c.*, c.value, d.value, r.value, s.value
    from (select * from data where data.population = 'C') c -- this reduces rows down to ~1000
    left join (select * from data where data.population = 'D') d on d.common_id = c.common_id
    left join (select * from data where data.population = 'R') r on r.common_id = c.common_id
    left join (select * from data where data.population = 'S') s on s.common_id = c.common_id
    ;

end

$$ language plpgsql stable;
    ;

平台信息:Ubuntu 14上的Postgres 9.3 LTS在4 GB ram和2 cpus的Vagrant VM上运行。 shared_buffers = 1000 MB(总数的1/4)和work_mem = 128 MB(开发机器,并行进行的并不多)。

那么,可能导致放缓的原因或我可以做些什么来进一步调查?

更新: 我将array_b的值作为文字粘贴到查询中(而不是使用变量),它运行正常。在视图中大约有16K的element_id,并且过滤约10K左右,似乎优化器决定在5次之后使用顺序扫描而不是索引扫描。这是我的怀疑,但我不确定。

1 个答案:

答案 0 :(得分:0)

由于查询/优化器更喜欢array_b的文字,我切换到动态sql,并将变量作为文字输入:

return query
execute format(
    'with data as (     -- this part of the query returns ~5000 rows and 40 columns
    select d.*
    from public.dashboard_data_view d  -- ~10 million rows in this view
    where d.organization_id = any (%L) -- array_a: 4 values in int[]
    and d.element_id = any (%L)        -- array_b: ~10,000 values in int[]
      )
    select c.*, c.value, d.value, r.value, s.value
    from (select * from data where data.population = ''C'') c -- this reduces rows down to ~1000
    left join (select * from data where data.population = ''D'') d on d.common_id = c.common_id
    left join (select * from data where data.population = ''R'') r on r.common_id = c.common_id
    left join (select * from data where data.population = ''S'') s on s.common_id = c.common_id
    ', array_a, array_b);

工作得很好!