解释计划成本与执行时间

时间:2008-09-19 14:52:36

标签: oracle optimization

之前,我发现执行计划中的“成本”是相对执行时间的一个很好的指标。为什么这种情况不同?我认为执行计划具有相关性我是个傻瓜吗?我具体可以尝试提高v_test性能吗?

谢谢。

使用Oracle 10g我有一个简单的查询视图,在下面定义

  create or replace view v_test as
  select distinct u.bo_id as bo_id, upper(trim(d.dept_id)) as dept_id
  from
      cust_bo_users u
  join cust_bo_roles r on u.role_name=r.role_name
  join cust_dept_roll_up_tbl d on 
                            (r.region is null or trim(r.region)=trim(d.chrgback_reg)) and 
                            (r.prod_id is null or trim(r.prod_id)=trim(d.prod_id)) and
                            (r.div_id is null or trim(r.div_id)=trim(d.div_id )) and
                            (r.clus_id is null or trim(r.clus_id )=trim( d.clus_id)) and
                            (r.prod_ln_id is null or trim(r.prod_ln_id)=trim(d.prod_ln_id)) and
                            (r.dept_id is null or trim(r.dept_id)=trim(d.dept_id))

定义为替换以下视图

        create or replace view v_bo_secured_detail
  select distinct Q.BO_ID, Q.DEPT_ID
  from (select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'REGION' and
                trim(R.REGION) = UPPER(trim(D.CHRGBACK_REG))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'RG_PROD' and
                trim(R.REGION) = UPPER(trim(D.CHRGBACK_REG)) and
                trim(R.PROD_ID) = UPPER(trim(D.PROD_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'PROD' and
                trim(R.PROD_ID) = UPPER(trim(D.PROD_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'DIV' and
                trim(R.DIV_ID) = UPPER(trim(D.DIV_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'RG_DIV' and
                trim(R.REGION) = UPPER(trim(D.CHRGBACK_REG)) and
                trim(R.DIV_ID) = UPPER(trim(D.DIV_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'CLUS' and
                trim(R.CLUS_ID) = UPPER(trim(D.CLUS_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'RG_CLUS' and
                trim(R.REGION) = UPPER(trim(D.CHRGBACK_REG)) and
                trim(R.CLUS_ID) = UPPER(trim(D.CLUS_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'PROD_LN' and
                trim(R.PROD_LN_ID) = UPPER(trim(D.PROD_LN_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(R.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'DEPT') Q

目标是删除对ROLE_LEVEL列的依赖。

对于简单的

,v_test的执行计划明显低于v_bo_secured_detail
select * from <view> where bo_id='value'

查询。并且在真实世界查询中使用时显着降低

  select CT_REPORT.RPT_KEY,
         CT_REPORT_ENTRY.RPE_KEY,
         CT_REPORT_ENTRY.CUSTOM16,
         Exp_Sub_Type.value,
         min(CT_REPORT_PAYMENT_CONF.PAY_DATE),
         CT_REPORT.PAID_DATE
  from CT_REPORT,
      <VIEW> SD,
      CT_REPORT_ENTRY,
      CT_LIST_ITEM_LANG Exp_Sub_Type,
      CT_REPORT_PAYMENT_CONF,
      CT_STATUS_LANG Payment_Status
  where (CT_REPORT_ENTRY.RPT_KEY = CT_REPORT.RPT_KEY) and
        (Payment_Status.STAT_KEY = CT_REPORT.PAY_KEY) and
        (Exp_Sub_Type.LI_KEY = CT_REPORT_ENTRY.CUSTOM9 and Exp_Sub_Type.LANG_CODE = 'en') and
        (CT_REPORT.RPT_KEY = CT_REPORT_PAYMENT_CONF.RPT_KEY) and
        (SD.BO_ID = 'JZHU9') and
        (SD.DEPT_ID = UPPER(CT_REPORT_ENTRY.CUSTOM5)) and
        (Payment_Status.name = 'Payment Confirmed' and (Payment_Status.LANG_CODE = 'en') and
        CT_REPORT.PAID_DATE > to_date('01/01/2008', 'mm/dd/yyyy') and Exp_Sub_Type.value != 'Korea')
  group by CT_REPORT.RPT_KEY,
            CT_REPORT_ENTRY.RPE_KEY,
            CT_REPORT_ENTRY.CUSTOM16,
            Exp_Sub_Type.value,
            CT_REPORT.PAID_DATE

执行时间差异很大。 v_test视图需要15个小时,而v_bo_secured_detail需要几秒钟。

 

谢谢所有回复的人

这是我要记住的一个。表达式的理论和数学符合基于硬件的执行的现实的地方。哎哟。

6 个答案:

答案 0 :(得分:4)

执行计划是理论,执行时间是现实。

该计划向您展示了引擎如何执行查询,但某些步骤可能会导致过多的工作来解决查询。 “x为null或x = y”的使用闻起来很糟糕。如果r和d是大表,你可能会遇到某种类型的组合爆炸,并且请求会通过大量的磁盘块循环无休止地循环。我想你在执行过程中会看到很多I / O.

另一方面,联合选择很短且很甜,因此可能会重复使用仍然存在于早期选择中的大量磁盘块,和/或您有一定程度的并行性受益于同一磁盘上的读取块。

在各地使用trim()和upper()也看起来有点可疑。如果您的数据非常不干净,那么可能需要定期进行一次定期清理,以便您可以说“x = y”并知道它有效。

更新:您要求提供改进v_test的提示。清理您的数据,以便trim()和upper()不必要。它们可能会阻止使用索引(尽管这也会影响联合的选择版本)。

如果你无法摆脱“x is null或x = y”那么y = nvl(x,'确实不存在')可能有更好的特征(假设'不存在'是一个“不可能发生”id值)。

答案 1 :(得分:3)

作为the Oracle documentation says,成本是相对于特定执行计划的估计成本。调整查询时,相对于计算成本的特定执行计划可能会发生变化。有时戏剧性。

v_test性能的问题在于Oracle除了执行嵌套循环之外无法执行它,对于每个cust_bo_roles,扫描所有cust_dept_roll_up_tbl以查找匹配项。如果表的大小为n和m,则需要n * m的时间,这对于大型表来说很慢。相比之下,v_bo_secured_detail被设置为一系列查询,每个查询都可以通过其他机制完成。 (Oracle有一个它可以使用的数字,包括使用索引,动态构建哈希,或者对数据集进行排序和合并。这些操作都是O(n * log(n))或更好。)一小部分快速查询很快。

尽管很痛苦,如果你想让这个查询更快,你需要像以前的查询一样将其分解出来。

答案 2 :(得分:0)

低成本 - 高执行时间的一个方面是,当您查看大型数据集时,整体上执行批量操作通常更有效,而如果您想要快速获得结果,那么更有效率地做尽可能少的工作来获得第一条记录。在处理大型集合时,执行快速响应外观的小操作的重复性不太可能产生良好的结果。

很多时候,当您想要快速结果时,USE_NL优化器提示将有所帮助。

此外,在您的测试视图中,它依赖于IS NULL ... IS NULL不能使用索引,也不能在'table-side'参数上使用trim等函数。

答案 3 :(得分:0)

您是否收集了所有基础表的优化程序统计信息?如果没有它们,优化者的估计可能与实际情况大相径庭。

答案 4 :(得分:0)

当您说“查询计划较低”时,您是说它更短,还是实际成本估算更低?替换视图的一个明显问题是与cust_dept_roll_up_tbl的连接几乎完全使用了不可索引的标准(索引可以满足“is null”测试,但是涉及对每个参数调用trim的那些测试不能),因此规划器必须对表进行至少一次,可能是几次顺序扫描才能满足查询。

我不确定Oracle是否有此限制,但是许多DB只能对每个包含的表执行单个索引扫描,因此即使您将连接条件清理为可索引,也可能只满足一个条件使用索引扫描并且必须对剩余部分使用顺序扫描。

答案 5 :(得分:0)

详细说明费用。

在Oracle 9 / 10g中,简化了一点,成本由公式确定:

成本=(SrCount * SrTime + MbrCount * MbrTime + CpuCyclesCount * CpuCycleTime)/ SrTime

其中SrCount - 计算单个块读取总数,SrTime - 根据收集的系统统计信息读取一个单个块的平均时间,MbrCount和MbrTime,相应地对多个块读取相同(在全表扫描和索引快速全扫描期间使用) ),Cpu相关指标是不言自明的......并且全部除以单个块读取时间。