暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Oracle-函数索引

205

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进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论