Part1环境
操作系统:redhat7.6
数据库:Oracle19C
架构:Rac集群
Part2问题
数据库CPU飙升一直在报警,通过AWR和ASH报告查到的TOP CPU SQL语句(其实19C生成AWR报告的话,ASH和ADDM也会生成出来),查到了问题SQL语句,也发现了是全表扫描。 但是后续观察的时候发现这个SQL语句的这个列有唯一索引。
涉及语句
SELECT COUNT(1) FROM AA where 1=1 and BB = :1
涉及索引
create unique index AA_KEY on AA (BB)
需要定位到底是什么原因
Part3定位
首先是收集表的统计信息
exec DBMS_STATS.GATHER_TABLE_STATS(OWNNAME => 'AA',TABNAME => 'BB',ESTIMATE_PERCENT => 80,METHOD_OPT => 'for all columns size repeat',no_invalidate => FALSE,DEGREE => 8,GRANULARITY => 'ALL',CASCADE => TRUE);
接着是查看统计信息是否更新
select owner,table_name,last_analyzed from dba_tables where table_name='BB';
然后是看执行计划
set autotrace on
set timing on
SELECT COUNT(1) FROM AA where 1=1 and BB = 1122;
COUNT(1)
----------
1
Elapsed: 00:00:00.53
Execution Plan
----------------------------------------------------------
Plan hash value: 1344375656
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 9 | 9395 (1)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 9 | | |
|* 2 | TABLE ACCESS FULL| AA | 1 | 9 | 9395 (1)| 00:00:01 |
-----------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(TO_NUMBER("BB")=00247516)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
34405 consistent gets
0 physical reads
0 redo size
550 bytes sent via SQL*Net to client
439 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
根据朋友们的提示 2 - filter(TO_NUMBER("BB")=00247516) 根据执行计划的这个条信息,代表数据库使用了隐式转换 也就是说AA表的这列是VARCHAR2(100),是字符,本来应该插入的也是字符,但是这里强制插入数字,就做了一次隐式转换,把字符转成数字。这种情况是无法使用唯一索引的。
字段是varchar2,传参1212是number,字段要做to_number。
Part4解决
解决办法有三个
4.1、 数字加加上单引号
SQL> SELECT COUNT(1) FROM AA where 1=1 and BB = '1212';
COUNT(1)
----------
0
Elapsed: 00:00:00.03
Execution Plan
----------------------------------------------------------
Plan hash value: 983086752
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 9 | 2 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 9 | | |
|* 2 | INDEX UNIQUE SCAN| AA_KEY | 1 | 9 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("BB"='1212')
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
3 consistent gets
1 physical reads
0 redo size
549 bytes sent via SQL*Net to client
437 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
4.2、 创建函数索引
CREATE INDEX FUN_AA ON AA(to_number(BB)) online parallel 6;
SQL语句条件的字段不加单引号的执行计划
SQL> set pages 999 lines 999
SQL> set autotrace on
SQL> set timing on
SQL> SELECT COUNT(1) FROM AA where 1=1 and BB = 1212;
COUNT(1)
----------
0
Elapsed: 00:00:00.04
Execution Plan
----------------------------------------------------------
Plan hash value: 1307389177
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 6 | 3 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 6 | | |
|* 2 | INDEX RANGE SCAN| FUN_BB | 1 | 6 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(TO_NUMBER("BB")=1212)
Statistics
----------------------------------------------------------
6 recursive calls
0 db block gets
7 consistent gets
0 physical reads
0 redo size
549 bytes sent via SQL*Net to client
435 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
4.3、 开发修改和定义
不允许传输数字,必须要求传输字符,从源端治理
Part5后记
学到了新知识马上去给大家漏一手,说我解决了,并附上了解决过程。结果还是接连服务器报警了一个小时,CPU居高不下,原来是这条SQL出现了两个执行计划,但我接着打算想办法的时候,领导强制让开发那边去修改传参,接着就消停了,感谢领导没让我漏出马脚。 附上查询的执行计划
select * from table(dbms_xplan.display_awr('aa481at337f7t'));
其实是不是强制绑定执行计划也可以?但是如果他输入的是字符了又该怎么办?
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




