遇到BITMAP CONVERSION TO ROWIDS 后解决与思考
今天遇到一个案例,有点价值写下来,以后多看看
SQL:
select t.order_id, t.spec_name, t.staff_code, t.staff_code as xxbStaffCode, t.channel_id as xxbChannelId,
t.area_code, t.create_time, t.id_card
from zmkm_intent_order t, zmkm_order_ext_val s
where t.order_id = s.order_id
and exists (select 1
from JS_SERVICE.zmkm_order_ext_val k
where k.order_Id = t.order_id
and k.param_id = '100007'
and k.ext_val = '18190044')
and t.deal_flag = 0
and t.area_code = '0516';
执行计划:
SQL的性能瓶颈在ID = 3.仔细看E-rows 才330条, 偏小很多。 但是为啥是 330呢。
分别explain plan for
select count(1) from zmkm_order_ext_val k where k.param_id = '100007';
select count(1) from zmkm_order_ext_val k where k.ext_val = '18190044'
rows 第一个是21万, 第二个是5千。但是BITMAP CONVERSION TO ROWIDS参与后就330.
就是因为rows 估算成330,导致这个搞成驱动表。 因此导致两个索引合并后真实数据量是9千, 大量的回表K。造成了性能问题。
如果这边搞成被驱动表, 那么显然就是根据索引访问K。并且此时驱动表的数据也不多,大概是 1000多条 NL下去也是不错的。
想到这边差不多知道答案了。期间尝试 数次 cardinality hint 无果。
SQL> alter session set "_b_tree_bitmap_plans"=false;
会话已更改。
已选择352行。
执行计划
----------------------------------------------------------
Plan hash value: 141468126
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5254 | 815K| 2288 (1)| 00:00:28 |
| 1 | NESTED LOOPS | | 5254 | 815K| 2288 (1)| 00:00:28 |
| 2 | NESTED LOOPS SEMI | | 330 | 44880 | 1627 (0)| 00:00:20 |
| 3 | TABLE ACCESS BY INDEX ROWID| ZMKM_INTENT_ORDER | 405 | 39285 | 7 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | IDX_ACODE_DFLAG | 405 | | 1 (0)| 00:00:01 |
|* 5 | TABLE ACCESS BY INDEX ROWID| ZMKM_ORDER_EXT_VAL | 269 | 10491 | 4 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | IDX_ORDER_ID | 16 | | 2 (0)| 00:00:01 |
|* 7 | INDEX RANGE SCAN | IDX_ORDER_ID | 16 | 368 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("T"."AREA_CODE"='0516' AND "T"."DEAL_FLAG"=0)
5 - filter("K"."EXT_VAL"='18190044' AND "K"."PARAM_ID"='100007')
6 - access("K"."ORDER_ID"="T"."ORDER_ID")
7 - access("T"."ORDER_ID"="S"."ORDER_ID")
统计信息
----------------------------------------------------------
1 recursive calls
0 db block gets
4518 consistent gets
0 physical reads
0 redo size
13485 bytes sent via SQL*Net to client
725 bytes received via SQL*Net from client
25 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
352 rows processed
alter session set "_b_tree_bitmap_plans"=false;--- 加入这个后逻辑读减少到 4518。
但是还有其他办法吗?? 答案是有的。
看到有问题的执行计划, 就是在两个索引合中取数据, 那么如果建立组合索引, 把该要的数据全部集中在索引中。
那么就没有必要在两个索引中取数据了。 建立索引 index ( PARAM_ID,EXT_VAL, ORDER_ID); 也就是9万多数据。
应该在7百个逻辑读左右。 结合另一张表访问也是7百个逻辑读。 不过此时hash 。 能控制在 1600 逻辑读左右。
1 适合用sqlprofile 立即绑定执行计划 ,适合线上快速改变执行计划
2 适合实施简单方便, 因为有些时候 DBA和业务是隔开的, 让业务绑定执行计划,反正我是没有去交接过。 不如扔一个建索引语句,说建立一下即可。