背景:
早上同事说,查询有一个页面出不来结果,登陆db上看正在执行的sql,看到有5个sql长的都差不多,已经执行了100s左右。同事说这个功能很重要,迫切需要优化。
基本情况:
SQL:稍微做简化处理
SELECTt.PROC_RESULT_ID as"ProcResultID", .... t.CreateName as "CreateName"FROM (SELECTROWNUM AS rowno, .....t6.FIRST_NAME AS CreateName
FROM PC_PR_HEADER t1, USR_RL_DM_MAP_ORG t2,
USR_RL_DM_MAP_XY URDM_ORG, USER_ROLE_MAP URI,
USER_PROFILE t5, CONTACT t6
WHERE (t2.ORG_ID = t1.OWNER_ORG_ID AND (URDM_ORG.ENTITY_ID =t2.ROOT_ORG_ID AND
(URI.ROLE_ID = URDM_ORG.URM_IDAND (URI.USER_ID ='USERORG000010001'))))
AND t5.ID = t1.CREATED_BY AND t6.ID = t5.CONTACT_ID
AND URDM_ORG.DOMAIN_TYPE_ID = 'CUST' AND ROWNUM <= 50.0
and t6.FIRST_NAME = '管理员二'
and t1.CREATED_DATE >= TO_DATE('06/01/2017 00:00:00 ', 'mm/dd/yyyyhh24:mi:ss')
and t1.CREATED_DATE <= TO_DATE('06/03/201723:59:59 ', 'mm/dd/yyyy hh24:mi:ss')
ORDER BY t1.CREATED_DATE DESC) t
WHERE t.rowno > 0
从等待事件分析:
几个会话的等待事件均为:latch cachebuffer chains 根据等待事件的p1raw找出具体对象:C000000552D2CD58是从等待事件中查询得出这里不多说了......
selecta.hladdr, a.file#, a.dbablk, a.tch, a.obj, b.object_name from x$bh a, dba_objects b
where (a.obj = b.object_id or a.obj =b.data_object_id)
anda.hladdr in ('C000000552D2CD58')
union
selecthladdr, file#, dbablk, tch, obj, null from x$bh
where obj in (select obj from x$bh where hladdr in ('C000000552D2CD58')
Minus select object_id from dba_objects
Minus select data_object_id from dba_objects)
and hladdr in ('C000000552D2CD58')
order by 4;

pc_pr_header是t1,file是74,数据块是3409135. tch为116,已经很高。再进一步确认热点块:
select * from (select CREATED_DATE,dbms_rowid.rowid_block_number(rowid)as blockid,
dbms_rowid.rowid_relative_fno(rowid) as fileid
from PC_PR_HEADER t1) where fileid = 74 and blockid = 3409135

执行计划:


确认到热点块在T1表中,根据SQL中的条件 进一步确认
WHERE t2.ORG_ID = t1.OWNER_ORG_ID ....
AND t5.ID = t1.CREATED_BY ....
and t1.CREATED_DATE >= TO_DATE('06/01/2017 00:00:00 ', 'mm/dd/yyyyhh24:mi:ss')
and t1.CREATED_DATE <= TO_DATE('06/03/201723:59:59 ', 'mm/dd/yyyy hh24:mi:ss')
结合执行计划中的是nested_loop 的被驱动表,父节点是ID = 7. 因此建立组合索引。 考虑到等值关联,和where条件使用频率和选择性。 建立索引
create index ind_pph_candel on PC_PR_HEADER(OWNER_ORG_ID,CREATED_BY,CREATED_DATE) online;
从SQl以及数据量上面分析
其中ID=7 是NESTED LOOPS ,id=8是驱动表,id=24是回表再过滤。
查询Id= 8有多少条数据
select count(*)
from USR_RL_DM_MAP_ORG t2, USR_RL_DM_MAP_XY URDM_ORG,USER_ROLE_MAP URI where URDM_ORG.ENTITY_ID = t2.ROOT_ORG_ID
AND URI.ROLE_ID = URDM_ORG.URM_ID
AND (URI.USER_ID = 'USERORG000010001')
and URDM_ORG.DOMAIN_TYPE_ID = 'CUST';
大概有1.3W;这和cbo算出来的1条是不同的
那就说明id=25要被扫描1.3w次了,而且每次都要回表再过滤,要回表1.3w次。而x$bh中tch为116的对象是表,而不是索引,那应该就是回表导致的热块了。
所以创建的组合索引,其原理把需要的数据放入索引中,之后直接在索引中过滤数据即可,而不是像现在再次回表后过滤。从而减少逻辑读。另外这边如果ID = 7 这边如果改成hash 关联,是否也能减少执行成本呢?(读者可以思考下)。
效果对比:

很明显执行成本减少很多, 原先100S+,也到秒杀了。
总结:
该案例从两个不同的角度出发,定位到同一个性能瓶颈,这要对系统级知识点有深度的考验。人说:旅途重要的不是目的地,而是沿途的风景。
同样分析问题结果重要,其过程更重要,因为在环环相扣的细节,分析其原因,探索其本质。然后有所收,有所获。




