SQL语句(0kym2mpab9k16):
SELECT COUNT(*)
FROM TF_BH_TRADE_CBSS A
WHERE A.TRADE_TYPE_CODE IN ('141', '142')
AND A.USER_ID = :B2
AND A.ACCEPT_DATE > :B1
AND A.SUBSCRIBE_STATE = '9'
我们从1号早上的AWR报告中看到这条语句开销非常大,初看之,这么简单的一条语句,从理论上来讲应当没什么问题,也不存在可优化的地方。仔细看一下报告,却可以发现,这条语句执行的次数并不多,才4500次,但是每次执行的gets却非常大,达到了51万。
如果我们参照语句的业务逻辑来理解一下,这个语句应当是根据user_id来进行统计,而每个user_id是应该有索引的,并且每个user_id对应的行数应该也是非常有限的。按照经验来推断,SQL执行的gets开销应该在几百以内,不应该上千,更不应该上万了。那应该是执行计划出问题了。
那接下来我们来看看执行计划:
select dbms_xplan.display_awr('0kym2mpab9k16') from dual ;
这下子比较明显了,有两个不同的执行计划,分别走了两个不同的索引,从索引的名字上也可以判断,一个走了user_id,一个走了acceptdate。那基本可以确定了,这个语句的问题就在于错用了accept_date字段上的索引,accept_date的选择性比较低,导致了大量的开销。
那如何解决呢?
1、优先应该考虑的是重新收集统计信息,相信Oracle在绝大部分情况下都是可以得出高效的执行计划的。很多这种情况是由于统计信息陈旧或者错误误导了Oracle,得到了错误的执行计划。
2、使用强制索引,然后更新重新发布代码
SELECT COUNT(*)
FROM TF_BH_TRADE_CBSS A
WHERE A.TRADE_TYPE_CODE IN ('141', '142')
AND A.USER_ID = :B2
AND A.ACCEPT_DATE > :B1
AND A.SUBSCRIBE_STATE = '9'
3、其他方式固定执行计划。