4

我们正在运行Postgres 9.3.5。 (07/2014) 我们有相当复杂的数据仓库/报告设置(ETL,物化视图,索引,聚合,分析功能等)。关于Postgres基于窗口的查询的错误优化/计划(按(,分组?)分区) - 1000x加速

我发现现在可能很难优化来实现,但它使得在性能上的巨大差异(巨大相似性只是示例代码,我们的查询,以减少不必要的复杂性)(?):

create view foo as 
select 
    sum(s.plan) over w_pyl as pyl_plan,  -- money planned to spend in this pot/loc/year 
    sum(s.booked) over w_pyl as pyl_booked, -- money already booked in this pot/loc/year 

    -- money already booked in this pot/loc the years before (stored as sum already) 
    last_value(s.booked_prev_years) over w_pl as pl_booked_prev_years,  

    -- update 2014-10-08: maybe the following additional selected columns 
    -- may be implementation-/test-relevant since they could potentially be determined 
    -- by sorting within the partition: 
    min(s.id) over w_pyl, 
    max(s.id) over w_pyl, 

    -- ... anything could follow here ... 
    x.*, 
    s.* 
from 
    pot_location_year x -- may be some materialized view or (cache/regular) table 
    left outer join spendings s 
    on (s.pot = x.pot and s.loc = x.loc and s.year = x.year) 
window 
    w_pyl as (partition by x.pot, x.year, x.loc) 
    w_pl as (partition by x.pot, x.loc order by x.year) 

我们有这两个相关指标到位:

pot_location_year_idx__p_y_l -- on pot, year, loc 
pot_location_year_idx__p_l_y -- on pot, loc, year 

现在我们运行一些测试查询

的解释
explain select * from foo fetch first 100 rows only 

这告诉我们一些非常糟糕的的性能,因为PYL索引用于,其中结果集有:-(受到不必要的排序两次(在最外层WindowAgg/Sort步排序因为这是必要的我们last_value(..) as pl_booked_prev_years):

Limit (cost=289687.87..289692.12 rows=100 width=512) 
    -> WindowAgg (cost=289687.87..292714.85 rows=93138 width=408) 
     -> Sort (cost=289687.87..289920.71 rows=93138 width=408) 
       Sort Key: x.pot, x.loc, x.year 
       -> WindowAgg (cost=1.25..282000.68 rows=93138 width=408) 
        -> Nested Loop Left Join (cost=1.25..278508.01 rows=93138 width=408) 
          Join Filter: ... 
          -> Nested Loop Left Join (cost=0.83..214569.60 rows=93138 width=392) 
           -> Index Scan using pot_location_year_idx__p_y_l on pot_location_year x (cost=0.42..11665.49 rows=93138 width=306) 
           -> Index Scan using ... (cost=0.41..2.17 rows=1 width=140) 
             Index Cond: ... 
          -> Index Scan using ... (cost=0.41..0.67 rows=1 width=126) 
           Index Cond: ... 

所以明显的问题是,该策划者应该选择现有一层索引,而不必排序两次

+0

我可以补充一点,我们有一些从Oracle迁移到Postgres数据库的地方,在Oracle数据库中,这个问题/查询似乎(!)是没有问题的。 (我知道还有很多影响规划和执行的其他因素)。 – 2014-10-07 13:49:01

+3

这可能值得在pgsql-performance邮件列表中提出。尽管我并不完全确定(a,b,c)和(c,a,b)在分割窗口时的语义上完全相同。 – 2014-10-07 13:55:15

+2

只是这样做的:http://postgresql.1045698.n5.nabble.com/Bad-optimization-planning-on-Postgres-window-based-queries-partition-by-group-by-1000x-speedup-td5822190.html – 2014-10-08 06:35:51

回答

4

幸运的是,我发现我可以给规划者的(隐含的)提示通过确保其他视图分区的列顺序/窗口更均匀虽然没有语义必须做到这一点。

下改变现在回到了我的预期摆在首位(该层指数的使用),以获得:

... 
window 
    -- w_pyl as (partition by x.pot, x.year, x.loc) -- showstopper (from above) 
    w_pyl as (partition by x.pot, x.loc, x.year) -- speedy 
    w_pl as (partition by x.pot, x.loc order by x.year) 

的快1000倍的表演结果:

Limit (cost=1.25..308.02 rows=100 width=512) 
    -> WindowAgg (cost=1.25..284794.82 rows=93138 width=408) 
     -> WindowAgg (cost=1.25..282000.68 rows=93138 width=408) 
       -> Nested Loop Left Join (cost=1.25..278508.01 rows=93138 width=408) 
        Join Filter: ... 
        -> Nested Loop Left Join (cost=0.83..214569.60 rows=93138 width=392) 
          -> Index Scan using pot_location_year_idx__p_l_y on pot_location_year x (cost=0.42..11665.49 rows=93138 width=306) 
          -> Index Scan using ... (cost=0.41..2.17 rows=1 width=140) 
           Index Cond: ... 
        -> Index Scan using ... (cost=0.41..0.67 rows=1 width=126) 
          Index Cond: ... 

更新2014-10-09:

与另一0

Tom Lane-2 wrote本(主要Postgres的开发者之一)(有可能涉及),我这里面临以及在2013-02相关窗函数问题皮克9.2.2:

... 在系统中关于窗口函数的智能 几乎没有,至今。因此,如果 需要进行优化,则必须在查询中手写 并将WHERE子句放在较低级别。

所以一些(有争议)上的窗函数的题目总体思路,数据仓库功能等可以在此处考虑:

以上是一个很好的语句来加强我的假设,当它是决定在一般项目和DWH环境下进行一些Oracle-> Postgres迁移,花费更多时间和金钱的风险相当高。 (尽管所研究的功能看起来足够了。)

我喜欢Postgres在重要的领域,远远超过Oracle,例如,在代码和其他事物的语法和清晰度方面(我猜即使是源代码和可维护性(在所有方面)也要好得多),但Oracle显然是资源优化,支持和工具方面更先进的参与者地区,当你在典型的CRUD管理之外处理更复杂的数据库功能时。

我估计开源Postgres(以及EnterpriseDB的topups)在这些领域的长期发展中将会迎​​头赶上,但这需要至少10年,也许只有当它被巨大的,利他1名全球玩家如谷歌等)

利他在这个意义上,如果推区域停留的“自由”,这些公司的利益必须是肯定别的地方(可能与添加了一些广告行随机 - 我想我们可以在这里和那里生活))


更新2014年10月13日:

正如我在以前的更新上面挂(2014年10月9日),优化问题及其解决方法解决方案在一个相当similiar方式上走(上述修正后)当你想查询与约束/过滤器(这里pot_id)上述观点:

explain select * foo where pot_id = '12345' fetch first 100 rows only 

...

Limit (cost=1.25..121151.44 rows=100 width=211) 
    -> Subquery Scan on foo (cost=1.25..279858.20 rows=231 width=211) 
     Filter: ((foo.pot_id)::text = '12345'::text) 
     -> WindowAgg (cost=1.25..277320.53 rows=203013 width=107) 
       -> WindowAgg (cost=1.25..271230.14 rows=203013 width=107) 
        -> Nested Loop Left Join (cost=1.25..263617.16 rows=203013 width=107) 
          -> Merge Left Join (cost=0.83..35629.02 rows=203013 width=91) 
           Merge Cond: ... 
           -> Index Scan using pot_location_year_idx__p_l_y on pot_location_year x (cost=0.42..15493.80 rows=93138 width=65) 
           -> Materialize (cost=0.41..15459.42 rows=33198 width=46) 
             -> Index Scan using ... (cost=0.41..15376.43 rows=33198 width=46) 
          -> Index Scan using ... (cost=0.42..1.11 rows=1 width=46) 
           Index Cond: ... 

而且在上面的链接的建议,如果你想“推下来“对抗窗口聚集之前INT /过滤器,你必须明确地做,在视图本身已,这将是有效的这种类型的查询,然后用另一个1000倍的加速比为100行:

create view foo as 
... 
where pot_id='12345' 
... 

...

Limit (cost=1.25..943.47 rows=100 width=211) 
    -> WindowAgg (cost=1.25..9780.52 rows=1039 width=107) 
     -> WindowAgg (cost=1.25..9751.95 rows=1039 width=107) 
       -> Nested Loop Left Join (cost=1.25..9715.58 rows=1039 width=107) 
        -> Nested Loop Left Join (cost=0.83..1129.47 rows=1039 width=91) 
          -> Index Scan using pot_location_year_idx__p_l_y on pot_location_year x (cost=0.42..269.77 rows=106 width=65) 
           Index Cond: ((pot_id)::text = '12345'::text) 
          -> Index Scan using ... (cost=0.41..8.10 rows=1 width=46) 
           Index Cond: ... 
        -> Index Scan using ... (cost=0.42..8.25 rows=1 width=46) 
          Index Cond: ... 

后一些视图参数努力这种做法将有助于加速某些查询约束那些列,但仍是相当呆板关于更一般的富视使用和查询优化。

:你可以在“参数这样的观点”把它(它的SQL)在(set-returning) table function(甲骨文相当于pipelined table function)。关于这方面的进一步细节可以在上面的论坛链接中找到。

+0

增加的细节和“第二级解决方法”到同一地区的在“第一级解决方法”之后出现的问题:约束不是“下推”查询视图,但有时可以通过将视图封装在集合返回函数中和另一个1000倍加速中来解决。 – 2014-10-13 06:37:48

+0

**另一个建议**,也许令人不满意,**解决方法**当然可以,**缓存无约束视图结果的物化视图**的帮助。 – 2014-10-13 06:50:28

+0

另一个有趣的评论:***换句话说,即使在简单的情况下,当前也不可能在View中使用Window函数,而不会冒着严重的,意外的性能问题!***:http://postgresql.1045698.n5。 nabble.com/Missing-optimization-when-filters-are-applied-after-window-functions-td5708856.html – 2014-10-13 11:10:51