一次TOP_SQL的性能调优经历

  • 格式:pdf
  • 大小:614.22 KB
  • 文档页数:35

1.问题发现1.1一份AWR报告今天,收到一份100个并发用户访问下压力测试的AWR报告,并发事务数平均每秒只有6个不到。

在这个26.53分钟间隔的报告里,CPU TIME在整个TOP 事件中最突出占了近97.6%,在8个CPU系统中,数据库给CPU造成的压力为:(5740/(26.53*60*8)*100%=45.07%,这么小的压力下,CPU就能冲得这么高,说明系统中肯定是有问题的。

下面转向TOP SQL去确认下造成资源争用最明显的SQL语句。

1.2TOP SQL1.2.1SQL ordered by Elapsed Time1.2.2SQL ordered by CPU Time1.2.3SQL ordered by Gets1.3问题发现对一个OLTP系统来说,每一个语句的执行,都是要将其消耗的资源降到最低,这跟 OLAP系统是有差别的。

对于后者来说,它需要的是短的时间返回结果,不管中间你会拿多大的成本做代价。

从上面反映的问题来看,我们的性能,无疑就是葬送在了SQL_ID为4841ajtgh43qy、1hwqh7kvxn6yg、g295mubwupf52和anyty5rts5tzf这四个语句上面,下面将对这四个SQL语句一 一做出分析,并给出相应的调优建议。

2.SQL_ID为4841ajtgh43qy的语句 2.1调整前2.1.1语句SELECT *FROM (SELECT TT.*, ROWNUM AS ROWNOFROM (select to_char(c.cust_id), c.passwordfrom CUST_CERTIFICATION cwhere c.cust_id = :CUST_ID) TTWHERE ROWNUM <= 10) TABLE_ALIAS where TABLE_ALIAS.rowno > 0;2.1.2解释计划又见全表扫2.1.3统计信息每次执行,buffer gets居然达37190.80之多。

2.1.4数值分布2.2原因分析从上面的情况分析来看,原因已经非常明显了,也就是在列cust_id上面,没有索引,导致每次执行该语句时,进行的都是全表扫描操作,这种操作对于一个600万的大表,在一个OLTP 系统中,是很致命的。

2.3调整2.3.1创建索引,并收集统计信息create index crm551.IDX_CUST_CERTIFICATION oncrm551.CUST_CERTIFICATION(cust_id);execute dbms_stats.gather_table_stats(ownname => 'CRM551',tabname => 'CUST_CERTIFICATION',cascade => true);2.3.2语句与原语句一致2.4调整后2.4.1解释计划2.4.2统计信息平均执行一次SQL的一致性读从37,190.80降低到5个,这个优化的效果是相当明显的。

3.SQL_ID为1hwqh7kvxn6yg的语句3.1调整前3.1.1语句SELECT COUNT(*) PAGE_SIZEFROM (select *from (SELECT A.CUST_ORDER_ID,A.ORDER_ITEM_ID,A.OFFER_ID,(SELECT PROD_OFFER_NAMEFROM PROD_OFFERWHERE PROD_OFFER_ID = A.OFFER_IDAND ROWNUM = 1) OFFER_NAME,(SELECT ACC_NBRFROM PROD_INST_551 C, ORDER_ITEM_REL E, ORDER_ITEM FWHERE E.Z_ORDER_ITEM_ID = F.ORDER_ITEM_IDAND E.A_ORDER_ITEM_ID = A.ORDER_ITEM_IDAND F.ORDER_ITEM_OBJ_ID = C.PROD_INST_IDAND F.ORDER_ITEM_CD IN ('13')AND ROWNUM < 2) ACC_NBR,TO_CHAR(A.HANDLE_TIME, 'yyyy-mm-dd hh24:mi:ss') ACCEPT_DATE,A.STATUS_CD,(SELECT CODE_NAMEFROM MAP_CODEWHERE CODE = A.STATUS_CDAND TABLE_NAME = 'ORDER_ITEM'AND CODE_TYPE = 'SOS'AND ROWNUM = 1) STATUS_NAME,A.SERVICE_OFFER_ID,(SELECT SERVICE_OFFER_NAMEFROM SERVICE_OFFERWHERE SERVICE_OFFER_ID = A.SERVICE_OFFER_ID) SERVICE_OFFER_NAMEFROM ORDER_ITEM A, CUSTOMER_ORDER B,PROD_OFFER_INST_551 DWHERE A.CUST_ORDER_ID = B.CUST_ORDER_IDAND A.ORDER_ITEM_OBJ_ID = D.PROD_OFFER_INST_IDAND B.CUST_ID = :CUST_IDAND TN_ID = :LATN_IDAND A.MAIN_FLAG = '1'AND A.PRE_HANDLE_FLAG <> '1'AND A.CUST_ORDER_ID = :CUST_ORDER_IDUNIONSELECT DISTINCT A.CUST_ORDER_ID,A.ORDER_ITEM_ID,A.OFFER_ID,(SELECT PROD_OFFER_NAMEFROM PROD_OFFERWHERE PROD_OFFER_ID = A.OFFER_IDAND ROWNUM = 1) OFFER_NAME,D.CUST_NUMBER,TO_CHAR(A.HANDLE_TIME,'yyyy-mm-dd hh24:mi:ss') ACCEPT_DATE,A.STATUS_CD,(SELECT CODE_NAMEFROM MAP_CODEWHERE CODE = A.STATUS_CDAND TABLE_NAME = 'ORDER_ITEM'AND CODE_TYPE = 'SOS'AND R OWNUM = 1) STATUS_NAME,A.SERVICE_OFFER_ID,(SELECT SERVICE_OFFER_NAMEFROM SERVICE_OFFERWHERE SERVICE_OFFER_ID =A.SERVICE_OFFER_ID) SERVICE_OFFER_NAMEFROM ORDER_ITEM A, CUSTOMER_ORDER B, CUST_551 DWHERE A.CUST_ORDER_ID = B.CUST_ORDER_IDAND A.ORDER_ITEM_OBJ_ID = D.CUST_IDAND B.CUST_ID = :CUST_ID2AND TN_ID = :LATN_ID2AND A.MAIN_FLAG = '1'AND A.PRE_HANDLE_FLAG <> '1'AND A.CUST_ORDER_ID = :CUST_ORDER_ID2UNIONSELECT DISTINCT A.CUST_ORDER_ID,A.ORDER_ITEM_ID,A.OFFER_ID,(SELECT PROD_OFFER_NAMEFROM PROD_OFFERWHERE PROD_OFFER_ID = A.OFFER_IDAND ROWNUM = 1) OFFER_NAME,D.ACCOUNT_NUMBER,TO_CHAR(A.HANDLE_TIME,'yyyy-mm-dd hh24:mi:ss')ACCEPT_DATE,A.STATUS_CD,(SELECT CODE_NAMEFROM MAP_CODEWHERE CODE = A.STATUS_CDAND TABLE_NAME = 'ORDER_ITEM'AND CODE_TYPE = 'SOS'AND ROWNUM = 1) STATUS_NAME,A.SERVICE_OFFER_ID,(SELECT SERVICE_OFFER_NAMEFROM SERVICE_OFFERWHERE SERVICE_OFFER_ID =A.SERVICE_OFFER_ID) SERVICE_OFFER_NAMEFROM ORDER_ITEM A, CUSTOMER_ORDER B, ACCOUNT_551 DWHERE A.CUST_ORDER_ID = B.CUST_ORDER_IDAND A.ORDER_ITEM_OBJ_ID = D.ACCOUNT_IDAND B.CUST_ID = :CUST_ID3AND TN_ID = :LATN_ID3AND A.MAIN_FLAG = '1'AND A.PRE_HANDLE_FLAG <> '1'AND A.CUST_ORDER_ID = :CUST_ORDER_ID3) torder by t.ACCEPT_DATE desc) T3.1.2解释计划3.1.3统计信息每次执行,buffer gets居然达20218.44之多。

3.1.4数值分布3.2原因分析这个语句看起来很吓人,其实没那么可怕。

碰到这种长语句,我们第一步要做的,就是把这个语句拆分,确认性能瓶颈出在哪个模块。

第一步:我们通过union分隔进行拆分,三个子句的绑定变量分别替换成常量值,分别执行 第二步:对比其统计信息发现,第二、三部分统计信息中的一致性读加起来只有30来个,其余的都是第一部分占用的,因此,我们可以锁定UNION子句的第一部分就是“元凶”。

第三步:再次拆分第一部分语句的,最后发现是SQL语句中的黑粗体部分导致大量的一致性读。

查看这部分的解释计划:果然,这部分一方面ORDER_ITEM_REL表做了全表扫描,然后做HASH JOIN的驱动表也不对。

因为只有ORDER_ITEM_REL才与外面的驱动表有关联条件AND E.A_ORDER_ITEM_ID = A.ORDER_ITEM_ID,因此要做HASH JOIN或NESTED LOOP,驱动表都应该是它而不应该是ORDER_ITEM。