一个SQL查询很慢,查看执行计划出现了较多的“BITMAP CONVERSION FROM ROWIDS”,如下所示。
SQL_ID 1x9rd10ykjvjd, child number 0
-------------------------------------
select t.*, t.LIMIT_TIME - SYSDATE AS nowNumber,
t.LIMIT_TIME - SYSDATE - i.manual_task_limit / 24 AS limitNumber from
USER_WAITING_TASK t, t_wfd_process_info i where i.del_flag = NVL('no',
UID) and t.processdefname = i.process_name_en and t.version =
i.version and app_type = 'platform' and (flow_typeid = '4')
and (participantID = '133762' or participantID = '1485') order by
t.workCreateTime desc
Plan hash value: 1934810794
--------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem |
--------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 17442 |00:04:27.14 | 1092K| 245K| | | |
| 1 | SORT ORDER BY | | 1 | 3775K| 17442 |00:04:27.14 | 1092K| 245K| 8345K| 1135K| 7417K (0)|
| 2 | CONCATENATION | | 1 | | 17442 |00:04:26.94 | 1092K| 245K| | | |
|* 3 | HASH JOIN | | 1 | 3702K| 17436 |00:04:07.86 | 853K| 227K| 897K| 897K| 1340K (0)|
|* 4 | TABLE ACCESS FULL | T_WFD_PROCESS_INFO | 1 | 394 | 394 |00:00:00.01 | 15 | 0 | | | |
|* 5 | HASH JOIN | | 1 | 563K| 17436 |00:04:07.84 | 853K| 227K| 50M| 4080K| 72M (0)|
|* 6 | HASH JOIN RIGHT OUTER | | 1 | 563K| 205K|00:04:02.38 | 705K| 227K| 18M| 4842K| 19M (0)|
|* 7 | TABLE ACCESS FULL | T_BNS_BUSINESS_INFO | 1 | 346K| 346K|00:00:01.16 | 76285 | 0 | | | |
|* 8 | HASH JOIN | | 1 | 554K| 205K|00:04:00.73 | 629K| 227K| 18M| 2738K| 20M (0)|
|* 9 | TABLE ACCESS BY INDEX ROWID | WFWIPARTICIPANT | 1 | 90495 | 207K|00:03:54.23 | 272K| 227K| | | |
| 10 | BITMAP CONVERSION TO ROWIDS | | 1 | | 274K|00:01:32.68 | 74960 | 74520 | | | |
| 11 | BITMAP AND | | 1 | | 21 |00:01:32.45 | 74960 | 74520 | | | |
| 12 | BITMAP OR | | 1 | | 22 |00:00:00.87 | 727 | 720 | | | |
| 13 | BITMAP CONVERSION FROM ROWIDS | | 1 | | 22 |00:00:00.85 | 724 | 720 | | | |
|* 14 | INDEX RANGE SCAN | WFWIPARTICIPANT_N3 | 1 | 219K| 281K|00:00:00.78 | 724 | 720 | | | |
| 15 | BITMAP CONVERSION FROM ROWIDS | | 1 | | 0 |00:00:00.01 | 3 | 0 | | | |
|* 16 | INDEX RANGE SCAN | WFWIPARTICIPANT_N3 | 1 | 219K| 0 |00:00:00.01 | 3 | 0 | | | |
| 17 | BITMAP CONVERSION FROM ROWIDS | | 1 | | 300 |00:01:31.50 | 74233 | 73800 | | | |
|* 18 | INDEX RANGE SCAN | WFWIPARTICIPANT_N4 | 1 | 219K| 27M|00:01:26.31 | 74233 | 73800 | | | |
|* 19 | TABLE ACCESS BY INDEX ROWID | WFWORKITEM | 1 | 176K| 1089K|00:00:04.72 | 357K| 24 | | | |
|* 20 | INDEX RANGE SCAN | IDX_WFWI_STATE | 1 | 178K| 1089K|00:00:00.62 | 4383 | 6 | | | |
|* 21 | TABLE ACCESS FULL | T_BPM_FORM_INFO | 1 | 970K| 1503K|00:00:03.87 | 147K| 2 | | | |
|* 22 | HASH JOIN | | 1 | 73402 | 6 |00:00:19.08 | 238K| 18158 | 897K| 897K| 1339K (0)|
|* 23 | TABLE ACCESS FULL | T_WFD_PROCESS_INFO | 1 | 394 | 394 |00:00:00.01 | 15 | 0 | | | |
|* 24 | HASH JOIN OUTER | | 1 | 11171 | 6 |00:00:19.08 | 238K| 18158 | 713K| 713K| 1520K (0)|
| 25 | NESTED LOOPS | | 1 | 10990 | 6 |00:00:15.90 | 162K| 16691 | | | |
| 26 | NESTED LOOPS | | 1 | 10990 | 13 |00:00:15.90 | 162K| 16691 | | | |
|* 27 | HASH JOIN | | 1 | 10990 | 17 |00:00:15.90 | 162K| 16691 | 1027K| 1027K| 1262K (0)|
|* 28 | TABLE ACCESS BY INDEX ROWID | WFWIPARTICIPANT | 1 | 10990 | 88 |00:00:14.58 | 17762 | 16689 | | | |
| 29 | BITMAP CONVERSION TO ROWIDS | | 1 | | 7128 |00:00:13.41 | 11160 | 11056 | | | |
| 30 | BITMAP AND | | 1 | | 1 |00:00:13.41 | 11160 | 11056 | | | |
| 31 | BITMAP OR | | 1 | | 22 |00:00:00.77 | 728 | 717 | | | |
| 32 | BITMAP CONVERSION FROM ROWIDS| | 1 | | 22 |00:00:00.75 | 725 | 717 | | | |
|* 33 | INDEX RANGE SCAN | WFWIPARTICIPANT_N3 | 1 | 26618 | 281K|00:00:00.69 | 725 | 717 | | | |
| 34 | BITMAP CONVERSION FROM ROWIDS| | 1 | | 0 |00:00:00.01 | 3 | 0 | | | |
|* 35 | INDEX RANGE SCAN | WFWIPARTICIPANT_N3 | 1 | 26618 | 0 |00:00:00.01 | 3 | 0 | | | |
| 36 | BITMAP CONVERSION FROM ROWIDS | | 1 | | 179 |00:00:12.57 | 10432 | 10339 | | | |
|* 37 | INDEX RANGE SCAN | WFWIPARTICIPANT_N4 | 1 | 26618 | 3337K|00:00:11.75 | 10432 | 10339 | | | |
|* 38 | TABLE ACCESS BY INDEX ROWID | WFWORKITEM | 1 | 47401 | 258K|00:00:01.19 | 144K| 2 | | | |
|* 39 | INDEX RANGE SCAN | IDX_WFWI_STATE | 1 | 47736 | 291K|00:00:00.15 | 1034 | 1 | | | |
|* 40 | INDEX RANGE SCAN | PK_WF_FORM_INFO_UQ3 | 17 | 1 | 13 |00:00:00.01 | 36 | 0 | | | |
|* 41 | TABLE ACCESS BY INDEX ROWID | T_BPM_FORM_INFO | 13 | 1 | 6 |00:00:00.01 | 12 | 0 | | | |
|* 42 | TABLE ACCESS FULL | T_BNS_BUSINESS_INFO | 1 | 346K| 346K|00:00:02.92 | 76295 | 1467 | | | |
--------------------------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("WI"."PROCESSDEFNAME"="I"."PROCESS_NAME_EN" AND "I"."VERSIONSIGN"="I"."VERSION")
4 - filter("I"."DEL_FLAG"=SYS_OP_C2C('no'))
5 - access("WI"."EXTEND7"="I"."PROC_INS_ID")
6 - access("WI"."ACTIVITYINSTID"="BU"."ACTIVE_ID")
7 - filter("BU"."ACTIVE_ID" IS NOT NULL)
8 - access("WI"."WORKITEMID"="WP"."WORKITEMID")
9 - filter(("WP"."ENDTIME" IS NULL AND INTERNAL_FUNCTION("WP"."PARTIINTYPE")))
14 - access("WP"."PARTICIPANTID"='133762')
16 - access("WP"."PARTICIPANTID"='1485')
18 - access("WP"."PARTIINTYPE"='GET')
filter(("WP"."PARTIINTYPE"='EXE' OR "WP"."PARTIINTYPE"='GET'))
19 - filter("WI"."EXTEND1" IS NULL)
20 - access("WI"."CURRENTSTATE"=4)
filter(("WI"."CURRENTSTATE"=4 OR "WI"."CURRENTSTATE"=10))
21 - filter(("I"."FLOW_TYPEID"='4' AND "I"."APP_TYPE"='platform'))
22 - access("WI"."PROCESSDEFNAME"="I"."PROCESS_NAME_EN" AND "I"."VERSIONSIGN"="I"."VERSION")
23 - filter("I"."DEL_FLAG"=SYS_OP_C2C('no'))
24 - access("WI"."ACTIVITYINSTID"="BU"."ACTIVE_ID")
27 - access("WI"."WORKITEMID"="WP"."WORKITEMID")
28 - filter(("WP"."ENDTIME" IS NULL AND INTERNAL_FUNCTION("WP"."PARTIINTYPE")))
33 - access("WP"."PARTICIPANTID"='133762')
35 - access("WP"."PARTICIPANTID"='1485')
37 - access("WP"."PARTIINTYPE"='EXE')
filter(("WP"."PARTIINTYPE"='EXE' OR "WP"."PARTIINTYPE"='GET'))
38 - filter("WI"."EXTEND1" IS NULL)
39 - access("WI"."CURRENTSTATE"=10)
filter(("WI"."CURRENTSTATE"=4 OR "WI"."CURRENTSTATE"=10))
40 - access("WI"."EXTEND7"="I"."PROC_INS_ID")
41 - filter(("I"."FLOW_TYPEID"='4' AND "I"."APP_TYPE"='platform'))
42 - filter("BU"."ACTIVE_ID" IS NOT NULL)
但是实际并没有建立位图索引,这个bitmap索引是哪来的呢?如此复杂的执行计划,看着都累,各个conversion转换步骤也务必消耗较多的CPU,实际这个paticipantid的数据量也确实比较大,达到28万,经历较多的查询转换和join,也势必会消耗较多的时间。再查看这两个涉及的索引,分析发现这两个索引不合理,唯一度极其不高,尤其是WFWIPARTICIPANT_N4这个索引,总共只有4个值,显然这个索引必须删掉,另一个索引WFWIPARTICIPANT_N3的数据也很不均衡,数据量小的行数只有个位数,数据量大的行数多达20-30万,甚至还有少数50万、80万、100万以上的,显然这个索引也可以删除,如果没有其他业务需求必须要这个索引,也可以一起删掉,同时只针对这个慢查询建组合索引。
继续以上话题,这个bitmap索引到底是怎么回事呢?它到底来自哪里?由于知识所限,只能带着满脑子疑问去网搜,结果基本明晰,如下摘抄几段网文。
1、出现这样的情况,是因为表中存在不适当的索引,这些索引列的唯一度不高,oracle就有可能选择两个这样的索引转为bitmap来执行;然后根据这两个索引的值再确认共同有的ROWID,最后再通过ROWID回表提取符合条件的数据
2、可以使用/+ opt_param('_b_tree_bitmap_plans','false') /hint 在sql级消除bitmap
3、惜分飞大侠的解释:oracle的cbo是根据cost来决定大小来选择合适的执行计划,当它计算获得通过bitmap的方式执行的时候cost会更小,它就会选择使用这样的执行计划。一般出现这样的情况,都是因为对表建立的不适当的index导致,特别是对表中的唯一度不高的列建立了index,然后oracle就有可能选择两个这样的列转为为bitmap来执行。根据oracle的执行计划,肯定是cost最小的,但是它很多时候忽略了一致性读等其他条件,导致这个执行计划并非像oracle想象的那样最优,因为把btree index转为为bitmap index执行,需要消耗更多的cpu
综合以上网文,显然应该采取的措施是,删除唯一度低的index,建立组合index。
保存好原索引创建语句,新建组合索引,删掉涉及的几个导致位图转换的索引后,执行计划变得极其简洁,如下。
SQL_ID 1x9rd10ykjvjd, child number 0
-------------------------------------
select t.*, t.LIMIT_TIME - SYSDATE AS nowNumber,
t.LIMIT_TIME - SYSDATE - i.manual_task_limit / 24 AS limitNumber from
USER_WAITING_TASK t, t_wfd_process_info i where i.del_flag = NVL('no',
UID) and t.processdefname = i.process_name_en and t.version =
i.version and app_type = 'platform' and (flow_typeid = '4')
and (participantID = '133762' or participantID = '1485') order by
t.workCreateTime desc
Plan hash value: 584196048
---------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem |
---------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 17468 |00:03:06.51 | 999K| 561K| | | |
| 1 | SORT ORDER BY | | 1 | 443K| 17468 |00:03:06.51 | 999K| 561K| 8345K| 1135K| 7417K (0)|
|* 2 | HASH JOIN | | 1 | 443K| 17468 |00:03:06.39 | 999K| 561K| 914K| 914K| 1277K (0)|
|* 3 | TABLE ACCESS FULL | T_WFD_PROCESS_INFO | 1 | 394 | 395 |00:00:00.01 | 15 | 0 | | | |
|* 4 | HASH JOIN | | 1 | 67508 | 17468 |00:03:06.37 | 999K| 561K| 43M| 4175K| 46M (0)|
|* 5 | HASH JOIN RIGHT OUTER | | 1 | 73421 | 205K|00:03:03.00 | 851K| 561K| 18M| 4842K| 19M (0)|
|* 6 | TABLE ACCESS FULL | T_BNS_BUSINESS_INFO | 1 | 346K| 347K|00:00:01.12 | 76285 | 0 | | | |
|* 7 | HASH JOIN | | 1 | 73421 | 205K|00:03:01.40 | 775K| 561K| 15M| 3059K| 17M (0)|
| 8 | INLIST ITERATOR | | 1 | | 207K|00:01:27.07 | 167K| 125K| | | |
|* 9 | TABLE ACCESS BY INDEX ROWID| WFWIPARTICIPANT | 4 | 101K| 207K|00:01:27.00 | 167K| 125K| | | |
|* 10 | INDEX RANGE SCAN | WFWIPARTICIPANT_N4_N3 | 4 | 245K| 209K|00:00:00.98 | 954 | 867 | | | |
|* 11 | TABLE ACCESS FULL | WFWORKITEM | 1 | 1373K| 1348K|00:01:32.33 | 607K| 436K| | | |
|* 12 | TABLE ACCESS FULL | T_BPM_FORM_INFO | 1 | 970K| 1508K|00:00:02.08 | 147K| 0 | | | |
---------------------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("WI"."PROCESSDEFNAME"="I"."PROCESS_NAME_EN" AND "I"."VERSIONSIGN"="I"."VERSION")
3 - filter("I"."DEL_FLAG"=SYS_OP_C2C('no'))
4 - access("WI"."EXTEND7"="I"."PROC_INS_ID")
5 - access("WI"."ACTIVITYINSTID"="BU"."ACTIVE_ID")
6 - filter("BU"."ACTIVE_ID" IS NOT NULL)
7 - access("WI"."WORKITEMID"="WP"."WORKITEMID")
filter((("WI"."CURRENTSTATE"=10 AND "WP"."PARTIINTYPE"='EXE') OR ("WI"."CURRENTSTATE"=4 AND "WP"."PARTIINTYPE"='GET')))
9 - filter("WP"."ENDTIME" IS NULL)
10 - access((("WP"."PARTIINTYPE"='EXE' OR "WP"."PARTIINTYPE"='GET')) AND (("WP"."PARTICIPANTID"='133762' OR "WP"."PARTICIPANTID"='1485')))
11 - filter((INTERNAL_FUNCTION("WI"."CURRENTSTATE") AND "WI"."EXTEND1" IS NULL))
12 - filter(("I"."FLOW_TYPEID"='4' AND "I"."APP_TYPE"='platform'))
对于数据量30万规模的id,查询时间明显减少,由原来的180秒左右,降至100秒以内,10万规模的id查询时间降至20秒左右,大部分查询降至秒级或一秒以内。鉴于这是一台比较繁忙的主机,CPU负荷较高,如此大数据量的交叉连接查询计算基本也算正常(另一台测试一体机已经全部降至10秒以内)。为进一步提高性能,也对数据做了详细分析,发现3000万数据里面有约一半的数据可以清理或转移,因此待数据清理后应该可以解决问题。
文章评论