无语的index hint:手工分配哈希区,5小时不出结果,优化后20分钟,hint
同事发来一个语句,说5个小时不出结果,我滴个神呀,想看看到底是什么垃圾语句造成的。于是叫同事发过来。不看不知道,一看吓一跳,3个表关联,强制使用了2个index hint,其中一个表9g,一个表67g,还有一个小表40Mb。无知的开发人员,以为走index就是快的,哎。。。
下面是同事发来的语句:
select /*+ parallel(t,4) index(a,IDX_COMMBASUBSHIST_1) index(b,IDX_COMMCMSERVHIST_1)*/
1,
t.DISC_ID,
t.DISC_LEV,
to_date(20140117082042, 'yyyymmddhh24miss'),
t.MSINFO_ID,
t.ORG_ID,
t.SERV_ID,
t.SUBS_ID,
t.OBJ_GRP_ID,
a.SUBS_CODE,
a.SUBS_STAT,
a.SUBS_STAT_REASON,
a.SUBS_STAT_DATE,
a.ACTION_ID,
a.ACTION_TYPE,
a.ACTION_EX_TYPE,
a.ACT_DATE,
a.REQ_ID,
a.STAFF_ID,
a.CMMS_CUST_CODE,
a.SPEED_VALUE,
b.ACC_NBR,
b.CUST_ID,
b.SERV_NBR,
b.CONSUME_GRADE,
b.SERV_LEV,
b.ACCOUNT_NBR,
b.CITY_VILLAGE_ID,
b.SERV_CHANNEL_ID,
b.SERV_STAT_ID,
b.CUST_CLASS_DL,
b.CUST_TYPE_ID,
b.USER_TYPE,
b.USER_CHAR,
b.PAYMENT_TYPE,
b.BILLING_TYPE,
b.PROD_ID,
b.PROD_CAT_ID,
b.EXCHANGE_ID,
b.SERV_COL1,
b.SERV_COL2,
b.AREA_ID,
b.SUBST_ID,
b.BRANCH_ID,
b.STOP_TYPE,
b.CUST_MANAGER_ID,
b.CREATE_DATE,
b.ADDRESS_ID,
b.SUBS_DATE,
b.OPEN_DATE,
b.MODI_STAFF_ID,
b.CMMS_CUST_ID,
b.CUST_NAME,
b.SALES_ID,
b.SALES_TYPE_ID,
b.SERV_ADDR_ID,
t.HIST_CREATE_DATE,
b.ARREAR_MONTH,
b.ARREAR_MONTH_LAST,
t.SALESTAFF_ID,
t.EHOME_TYPE,
t.EHOME_CLASS,
b.strat_grp_dl,
b.sale_org1,
b.sale_org2,
b.sale_org3,
b.location_type,
b.region_flag,
b.terminal_id,
b.pstn_id,
b.fee_id,
b.payment_id,
b.billing_id,
b.strat_grp_xl,
b.fld1,
b.fld3,
b.cust_level,
b.group_cust_type,
b.cust_region,
b.group_cust_grade,
b.control_level,
b.net_connect_type,
b.trade_type_id,
b.acc_nbr2,
b.cdma_class_id,
b.phone_number_id,
b.develop_channel,
b.online_time,
t.wireless_type,
b.new_serv_stat_id,
b.is_phs_tk,
b.serv_grp_type,
b.state,
t.cdma_disc_type,
b.mix_disc,
b.is_3g,
t.add_disc_type,
to_number(nvl(b.business_type, '-1')),
nvl(t.label_num, -1),
b.is_mix_prod,
t.price_id,
t.disc_item_id,
b.STD_SUBST_ID,
b.STD_BRANCH_ID,
t.DISC_ITEM_ID_OP,
t.PRICE_ID_OP,
t.business_type,
b.new_prod_id,
b.BOARD_SUBST_ID,
b.BOARD_BRANCH_ID
from RPT_COMM_BA_SUBS_HIST a,
RPT_COMM_CM_SERV_HIST b,
TB_COMM_BA_MSDISC_TEMP t
where a.subs_id = t.subs_id
and b.serv_id = t.serv_id
--同事说开销比较大。有450W。。下面是执行计划:
<img src="http://img.blog.csdn.net/20141025102001913?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ2RtemxoajE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
/*
涉及的表大小:
OWNER SEGMENT_NAME SEGMENT_TYPE Size(Mb)
SUMMARY_SJZ_GZ TB_COMM_BA_MSDISC_TEMP TABLE 40
SUMMARY_SJZ_GZ RPT_COMM_CM_SERV_HIST TABLE PARTITION 9016.1875
SUMMARY_SJZ_GZ RPT_COMM_BA_SUBS_HIST TABLE PARTITION 67330.25
以下是优化思路:
强制使用索引,导致其中9g的表走了index full scan,然后回表。因为除了index fast scan以外,其他索引扫描都是单块读,回表又是单块读。导致速度非常慢。优化时考虑使用哈希连接,40Mb的小表作为驱动表,连接9g的表,最后连接超大的67G的表。
优化时使用的技术:
1. use_hash(a,b),使用哈希表关联方式
2. /*+parallel(a 5)*/;并行处理
3. db_file_multiblock_read_count多块读参数设置为最大
4. workarea_size_policy设置为手工管理
5. sort_area_size设为接近最大
6. hash_area_size设为接近最大
<p>5小时不出结果,优化后20分钟不到出结果,就是这么神奇。</p><p>alter session enable parallel dml;
alter session set workarea_size_policy=manual;
alter session set sort_area_size=2100000000;
alter session set hash_area_size=2100000000;
alter session set db_file_multiblock_read_count=128;
select /*+parallel(a,5) parallel(b,5) parallel(t,5) leading(t) use_hash(t,b) user_hash(b,a)*/
1,
t.DISC_ID,
t.DISC_LEV,
to_date(20140117082042, 'yyyymmddhh24miss'),
t.MSINFO_ID,
t.ORG_ID,
t.SERV_ID,
t.SUBS_ID,
t.OBJ_GRP_ID,
a.SUBS_CODE,
a.SUBS_STAT,
a.SUBS_STAT_REASON,
a.SUBS_STAT_DATE,
a.ACTION_ID,
a.ACTION_TYPE,
a.ACTION_EX_TYPE,
a.ACT_DATE,
a.REQ_ID,
a.STAFF_ID,
a.CMMS_CUST_CODE,
a.SPEED_VALUE,
b.ACC_NBR,
b.CUST_ID,
b.SERV_NBR,
b.CONSUME_GRADE,
b.SERV_LEV,
b.ACCOUNT_NBR,
b.CITY_VILLAGE_ID,
b.SERV_CHANNEL_ID,
b.SERV_STAT_ID,
b.CUST_CLASS_DL,
b.CUST_TYPE_ID,
b.USER_TYPE,
b.USER_CHAR,
b.PAYMENT_TYPE,
b.BILLING_TYPE,
b.PROD_ID,
b.PROD_CAT_ID,
b.EXCHANGE_ID,
b.SERV_COL1,
b.SERV_COL2,
b.AREA_ID,
b.SUBST_ID,
b.BRANCH_ID,
b.STOP_TYPE,
b.CUST_MANAGER_ID,
b.CREATE_DATE,
b.ADDRESS_ID,
b.SUBS_DATE,
b.OPEN_DATE,
b.MODI_STAFF_ID,
b.CMMS_CUST_ID,
b.CUST_NAME,
b.SALES_ID,
b.SALES_TYPE_ID,
b.SERV_ADDR_ID,
t.HIST_CREATE_DATE,
b.ARREAR_MONTH,
b.ARREAR_MONTH_LAST,
t.SALESTAFF_ID,
t.EHOME_TYPE,
t.EHOME_CLASS,
b.strat_grp_dl,
b.sale_org1,
b.sale_org2,
b.sale_org3,
b.location_type,
b.region_flag,
b.terminal_id,
b.pstn_id,
b.fee_id,
b.payment_id,
b.billing_id,
b.strat_grp_xl,
b.fld1,
b.fld3,
b.cust_level,
b.group_cust_type,
b.cust_region,
b.group_cust_grade,
b.control_level,
b.net_connect_type,
b.trade_type_id,
b.acc_nbr2,
b.cdma_class_id,
b.phone_number_id,
b.develop_channel,
b.online_time,
t.wireless_type,
b.new_serv_stat_id,
b.is_phs_tk,
b.serv_grp_type,
b.state,
t.cdma_disc_type,
b.mix_disc,
b.is_3g,
t.add_disc_type,
to_number(nvl(b.business_type, '-1')),
nvl(t.label_num, -1),
b.is_mix_prod,
t.price_id,
t.disc_item_id,
b.STD_SUBST_ID,
b.STD_BRANCH_ID,
t.DISC_ITEM_ID_OP,
t.PRICE_ID_OP,
t.business_type,
b.new_prod_id,
b.BOARD_SUBST_ID,
b.BOARD_BRANCH_ID
from SUMMARY_SJZ_GZ.RPT_COMM_BA_SUBS_HIST a,
SUMMARY_SJZ_GZ.RPT_COMM_CM_SERV_HIST b,
SUMMARY_SJZ_GZ.TB_COMM_BA_MSDISC_TEMP t
where a.subs_id = t.subs_id
and b.serv_id = t.serv_id
</p></pre><pre>
在SQL语句优化过程中,经常会用到hint,
以下是在SQL优化过程中常见Oracle中"HINT"的30个用法1. /*+ALL_ROWS*/
表明对语句块选择基于开销的优化方法,并获得最佳吞吐量,使资源消耗最小化.
例如:
SELECT /*+ALL+_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';
2. /*+FIRST_ROWS*/
表明对语句块选择基于开销的优化方法,并获得最佳响应时间,使资源消耗最小化.
例如:
SELECT /*+FIRST_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';
3. /*+CHOOSE*/
表明如果数据字典中有访问表的统计信息,将基于开销的优化方法,并获得最佳的吞吐量;
表明如果数据字典中没有访问表的统计信息,将基于规则开销的优化方法;
例如:
SELECT /*+CHOOSE*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';
4. /*+RULE*/
表明对语句块选择基于规则的优化方法.
例如:
SELECT /*+ RULE */ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';
5. /*+FULL(TABLE)*/
表明对表选择全局扫描的方法.
例如:
SELECT /*+FULL(A)*/ EMP_NO,EMP_NAM FROM BSEMPMS A WHERE EMP_NO='SCOTT';
6. /*+ROWID(TABLE)*/
提示明确表明对指定表根据ROWID进行访问.
例如:
SELECT /*+ROWID(BSEMPMS)*/ * FROM BSEMPMS WHERE ROWID>='AAAAAAAAAAAAAA'
AND EMP_NO='SCOTT';
7. /*+CLUSTER(TABLE)*/
提示明确表明对指定表选择簇扫描的访问方法,它只对簇对象有效.
例如:
SELECT /*+CLUSTER */ BSEMPMS.EMP_NO,DPT_NO FROM BSEMPMS,BSDPTMS
WHERE DPT_NO='TEC304' AND BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
8. /*+INDEX(TABLE INDEX_NAME)*/
表明对表选择索引的扫描方法.
例如:
SELECT /*+INDEX(BSEMPMS SEX_INDEX) USE SEX_INDEX BECAUSE THERE ARE FEWMALE BSEMPMS */ FROM BSEMPMS WHERE SEX='M';
9. /*+INDEX_ASC(TABLE INDEX_NAME)*/
表明对表选择索引升序的扫描方法.
例如:
......余下全文>>
1. /*+ALL_ROWS*/
表明对语句块选择基于开销的优化方法,并获得最佳吞吐量,使资源消耗最小化.
例如:
SELECT /*+ALL+_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO=’SCOTT’;
2. /*+FIRST_ROWS*/
表明对语句块选择基于开销的优化方法,并获得最佳响应时间,使资源消耗最小化.
例如:
SELECT /*+FIRST_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO=’SCOTT’;
3. /*+CHOOSE*/
表明如果数据字典中有访问表的统计信息,将基于开销的优化方法,并获得最佳的吞吐量;
表明如果数据字典中没有访问表的统计信息,将基于规则开销的优化方法;
例如:
SELECT /*+CHOOSE*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO=’SCOTT’;
4. /*+RULE*/
表明对语句块选择基于规则的优化方法.
例如:
SELECT /*+ RULE */ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO=’SCOTT’;
5. /*+FULL(TABLE)*/
表明对表选择全局扫描的方法.
例如:
SELECT /*+FULL(A)*/ EMP_NO,EMP_NAM FROM BSEMPMS A WHERE EMP_NO=’SCOTT’;
6. /*+ROWID(TABLE)*/
提示明确表明对指定表根据ROWID进行访问.
例如:
SELECT /*+ROWID(BSEMPMS)*/ * FROM BSEMPMS WHERE ROWID>=’AAAAAAAAAAAAAA’
AND EMP_NO=’SCOTT’;
7. /*+CLUSTER(TABLE)*/
提示明确表明对指定表选择簇扫描的访问方法,它只对簇对象有效.
例如:
SELECT /*+CLUSTER */ BSEMPMS.EMP_NO,DPT_NO FROM BSEMPMS,BSDPTMS
WHERE DPT_NO=’TEC304′ AND BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
8. /*+INDEX(TABLE INDEX_NAME)*/
表明对表选择索引的扫描方法.
例如:
SELECT /*+INDEX(BSEMPMS SEX_INDEX) USE SEX_INDEX BECAUSE THERE ARE FEWMALE BSEMPMS */ FROM BSEMPMS WHERE SEX=’M';
9. /*+INDEX_ASC(TABLE INDEX_NAME)*/
表明对表选择索引升序的扫描方法.
例如:
SELECT /*+INDEX_ASC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO=’SCOTT’;
10. /*+INDEX_COMBINE*/
为指定表选择位图访问路经,如果INDEX......余下全文>>