如何监控ORACLE索引使用与否

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何监控ORACLE索引使用与否相关的知识,希望对你有一定的参考价值。

  在数据库管理与维护中,我们总会遇到一个问题:我们创建的索引是否会被某些SQL语句使用呢?换个通俗表达方式:我创建的索引是否是未使用的索引(unused Indexes),是否有价值呢?如果创建的某个索引是Unused Indexes,尤其是没有合理规划索引的系统或那些管理控制不规范的系统。有可能建立了N个索引,其实有些索引都是没有任何SQL会使用,那么此时这些多余的索引其实会带来两个问题:1:浪费存储空间,尤其是大表的索引,浪费的存储空间尤其可观; 2:加重DML操作(UPDATE、INSERT、DELETE)的开销。
  ORACLE其实提供了监控索引使用情况的功能。ALTER INDEX <index_name> MONITORING USAGE; 我们下面来测试验证一下吧。
  创建一个表TEST作为实验测试验证的样例
  CREATE TABLE TEST
  (
  ID NUMBER(10),
  NAME VARCHAR2(32)
  );
  CREATE INDEX IDX_TEST_ID ON TEST(ID);
  INSERT INTO TEST
  SELECT 1001, \'Kerry\' FROM DUAL UNION ALL
  SELECT 1002, \'Ken\' FROM DUAL UNION ALL
  SELECT 1003, \'Jimmy\' FROM DUAL UNION ALL
  SELECT 1004, \'Jack\' FROM DUAL;
  COMMIT;
  execute dbms_stats.gather_table_stats(ownname => \'ETL\', tabname =>\'TEST\', estimate_percent =>DBMS_STATS.AUTO_SAMPLE_SIZE, method_opt => \'FOR ALL COLUMNS SIZE AUTO\');启用对索引IDX_TEST_ID的监控
  ALTER INDEX IDX_TEST_ID MONITORING USAGE;此时观察V$OBJECT_USAGE表数据的变化,如下所示,MONITORIN字段值变为YES,表示索引IDX_TEST_ID已经被置于监控状态。USED字段为NO表示暂时没有SQL使用该索引SQL> COL INDEX_NAME FOR A20
  SQL> COL TABLE_NAME FOR A10
  SQL> COL MONITORING FOR A10
  SQL> COL USED FOR A10
  SQL> COL START_MONITORING FOR A20
  SQL> COL END_MONITORING FOR A20
  SQL> SELECT * FROM V$OBJECT_USAGE;
  INDEX_NAME TABLE_NAME MONITORING USED START_MONITORING END_MONITORING------------ ---------- ---------- ---- -------------------- ----------------IDX_TEST_ID TEST YES NO 11/28/2015 14:57:41此时我们执行下面SQL,因为此时使用全表扫描,那么索引IDX_TEST_ID依然没有被使用,此时可以查看V$OBJECT_USAGE进行验证。
  SQL> SET AUTOTRACE ON;
  SQL> SELECT * FROM TEST WHERE ID =1001;
  ID NAME
  ---------- --------------------------------1001 Kerry
  Execution Plan
  --------------------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost |----------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 9 | 2 || 1 | TABLE ACCESS FULL| TEST | 1 | 9 | 2 |----------------------------------------------------------Note
  -----
  - \'PLAN_TABLE\' is old version
  Statistics
  ----------------------------------------------------------1 recursive calls
  0 db block gets
  4 consistent gets
  0 physical reads
  0 redo size
  578 bytes sent via SQL*Net to client
  492 bytes received via SQL*Net from client2 SQL*Net roundtrips to/from client
  0 sorts (memory)
  0 sorts (disk)
  1 rows processed
  clip_image001
  如下所示,此时索引IDX_TEST_ID依然没有被使用。
  clip_image002
  我们使用索引提示强制下面SQL使用索引IDX_TEST_IDSELECT /*+ INDEX(TEST IDX_TEST_ID) */* FROM TEST WHERE ID =1001;此时你就会发现USED的值变为了YES了。
  clip_image003
  ALTER INDEX IDX_TEST_ID NOMONITORING USAGE;执行上面命令后,在V$OBJECT_USAGE表中,就会更新表TEST记录的END_MONITORING、MONITORING的值。
  clip_image004
  如果你又启用监控索引使用情况,那么系统会更新START_MONITORING、END_MONITORING字段的值(END_MONITORING的值更新为NULL)。如果删除表TEST,此时你会发现V$OBJECT_USAGE对象中关于表TEST的记录也不见了。
  注意:SELECT * FROM V$OBJECT_USAGE; 只能查看当前用户下被监控的索引信息。即使sys、system用户也不能查看其它用户的信息。
  在测试过程中有个小疑问,就是在准备测试环境时,如果不对表收集统计信息的话,那么即使SQL走全表扫描,你依然发现V$OBJECT_USAGE中索引被标记使用了。如下所示DROP TABLE TEST PURGE;
  CREATE TABLE TEST
  (
  ID NUMBER(10),
  NAME VARCHAR2(32)
  );
  CREATE INDEX IDX_TEST_ID ON TEST(ID);
  INSERT INTO TEST
  SELECT 1001, \'Kerry\' FROM DUAL UNION ALL
  SELECT 1002, \'Ken\' FROM DUAL UNION ALL
  SELECT 1003, \'Jimmy\' FROM DUAL UNION ALL
  SELECT 1004, \'Jack\' FROM DUAL;
  COMMIT;
  ALTER INDEX IDX_TEST_ID MONITORING USAGE;SQL> SET AUTOTRACE ON;
  SQL> SELECT * FROM TEST WHERE ID =1001;
  ID NAME
  ---------- --------------------------------1001 Kerry
  Execution Plan
  --------------------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost |----------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 31 | 2 || 1 | TABLE ACCESS FULL| TEST | 1 | 31 | 2 |----------------------------------------------------------Note
  -----
  - \'PLAN_TABLE\' is old version
  Statistics
  ----------------------------------------------------------7 recursive calls
  0 db block gets
  10 consistent gets
  0 physical reads
  0 redo size
  578 bytes sent via SQL*Net to client
  492 bytes received via SQL*Net from client2 SQL*Net roundtrips to/from client
  0 sorts (memory)
  0 sorts (disk)
  1 rows processed
  SQL> SELECT * FROM V$OBJECT_USAGE;
  INDEX_NAME TABLE_NAME MONITORING USED START_MONITORING END_MONITORING------------ ---------- ---------- ------ ----------- -------------------IDX_TEST_ID TEST YES YES 11/28/2015 15:11:46那么为什么呢? 猜测是在解析生成执行计划时,用到了索引的一些信息,导致V$OBJECT_USAGE表中的字段USED被标记为YES。
  如果我们想在系统中监控所有的索引,那么我们可以通过下面脚本实现监控数据库所有的索引。注意我们要排除一些系统表的索引、以及LOB indexes。原因有下面两个:
  1:LOB indexes不能修改,否则会报ORA-22864错误(ORA-22864: cannot ALTER or DROP LOB indexes)。
  2:ORA-00701: object necessary for warmstarting database cannot be alteredORA-00701: object necessary for warmstarting database cannot be altered00701. 00000 - "object necessary for warmstarting database cannot be altered"*Cause: Attempt to alter or drop a database object (table, cluster, orindex) which are needed for warmstarting the database.
  *Action: None.
  SET PAGES 999;
  SET HEADING OFF;
  SPOOL run_monitor.sql
  SELECT
  \'ALTER INDEX \'||OWNER||\'.\'||INDEX_NAME||\' MONITORING USAGE;\'
  FROM
  DBA_INDEXES
  WHERE
  INDEX_TYPE != \'LOB\' AND OWNER NOT IN (\'SYS\', \'SYSMAN\', \'SYSTEM\', \'MDSYS\', \'WMSYS\', \'TSMSYS\', \'DBSNMP\', \'OUTLN\');
  SPOOL OFF;
  @run_monitor.sql
  此时使用下面脚本就能查出那些索引是未使用索引,当然监控索引时长非常重要,太短的话有可能导致查询出来的数据有问题,一般建议监控一周后即可,OLAP系统则需要适当延长监控的时间。
  SELECT I.TABLE_OWNER,
  T.TABLE_NAME,
  I.INDEX_NAME,
  U.USED,
  U.START_MONITORING,
  U.END_MONITORING
  FROM USER_TABLES T
  INNER JOIN USER_INDEXES I
  ON T.TABLE_NAME = I.TABLE_NAME
  INNER JOIN V$OBJECT_USAGE U
  ON U.TABLE_NAME = I.TABLE_NAME
  AND I.INDEX_NAME = U.INDEX_NAME
  WHERE I.TABLE_OWNER=SYS_CONTEXT(\'USERENV\',\'CURRENT_USER\')另外,博客Oracle - Find unused Indexes中介绍了一个查找没有使用索引的SQL语句。如下所示statspack_unused_indexes.sqlcol owner heading "Index Owner" format a30col index_name heading "Index Name" format a30set linesize 95 trimspool on pagesize 80
  select *
  from
  (select
  owner,
  index_name
  from
  dba_indexes di
  where
  di.index_type != \'LOB\'
  and
  owner not in (\'SYS\', \'SYSMAN\', \'SYSTEM\', \'MDSYS\', \'WMSYS\', \'TSMSYS\', \'DBSNMP\', \'OUTLN\')minus
  select
  index_owner owner,
  index_name
  from
  dba_constraints dc
  where
  index_owner not in (\'SYS\', \'SYSMAN\', \'SYSTEM\', \'MDSYS\', \'WMSYS\', \'TSMSYS\', \'DBSNMP\', \'OUTLN\')minus
  select
  p.object_owner owner,
  p.object_name index_name
  from
  stats$snapshot sn,
  stats$sql_plan p,
  stats$sql_summary st,
  stats$sql_plan_usage spu
  where
  st.sql_id = spu.sql_id
  and
  spu.plan_hash_value = p.plan_hash_value
  and
  st.hash_value = p.plan_hash_value
  and
  sn.snap_id = st.snap_id
  and
  sn.dbid = st.dbid
  and
  sn.instance_number = st.instance_number
  and
  sn.snap_id = spu.snap_id
  and
  sn.dbid = spu.snap_id
  and
  sn.instance_number = spu.instance_number
  and
  sn.snap_id between &begin_snap and &end_snapand
  p.object_type = \'INDEX\'
  )
  where owner not in (\'SYS\', \'SYSMAN\', \'SYSTEM\', \'MDSYS\', \'WMSYS\', \'TSMSYS\', \'DBSNMP\', \'OUTLN\')order by 1, 2
  /
  这里是另一个脚本用来跟踪未使用的索引并展示给所有索引的调用计数。最重要的是,这个脚本显示了多列索引引用的列(这个脚本执行时间较长,资源开销较大。)col c1 heading \'Begin|Interval|time\' format a20col c2 heading \'Search Columns\' format 999col c3 heading \'Invocation|Count\' format 99,999,999break on c1 skip 2
  accept idxname char prompt \'Enter Index Name: \'
  ttitle \'Invocation Counts for index|&idxname\'
  select
  to_char(sn.begin_interval_time,\'yy-mm-dd hh24\') c1,p.search_columns c2,count(*) c3from
  dba_hist_snapshot sn,
  dba_hist_sql_plan p,
  dba_hist_sqlstat st
  where
  st.sql_id = p.sql_id
  and
  sn.snap_id = st.snap_id
  and
  p.object_name = \'&idxname\'
  group by
  begin_interval_time,search_columns;
参考技术A 监控索引被用了多少次,这个我不清楚怎么实现,但你要监控索引是否被使用你可以按一下方法进行监控。

你要监控索引是否被使用,你需要提前开启监控索引
alter index *** monitoring usage; -- ***是你的索引名
然后你可以可以对这条索引进行监控了
select * from v$object_usage 这个语句就能进行监控了。
如果在你开启监控后使用了索引这个视图会有记录,否则无记录

Oracle - 位图索引的适用条件

位图索引的适用条件

位图索引适合只有几个固定值的列,如性别、婚姻状况、行政区等等,而身份证号这种类型不适合用位图索引。

位图索引适合静态数据,而不适合索引频繁更新的列。

 

举个例子,有这样一个字段busy,记录各个机器的繁忙与否,当机器忙碌时,busy为1,当机器不忙碌时,busy为0。

这个时候有人会说使用位图索引,因为busy只有两个值。好, 我们使用位图索引索引busy字段!假设用户A使用update更新某个机器的busy值,比如update table set table.busy=1 where rowid=100;,但还没有commit,而用户B也使用update更新另一个机器的busy值,update table set table.busy=1 where rowid=12; 这个时候用户B怎么也更新不了,需要等待用户A commit。

原因:用户A更新了某个机器的busy值为1,会导致所有busy为1的机器的位图向量发生改变,因此数据库会将busy=1的所有行锁定,只有commit之后才解锁。

 

问题: 为什么更新了某个机器的busy值为1,会导致所有busy为1的机器的位图向量发生改变,因此数据库会将busy=1的所有行锁定?

答案请看位图索引的存储结构。

位图的存储,位图中位的个数和表中的记录数项目,

比如下面婚姻状况表

姓名(Name) 性别(Gender) 婚姻状况(Marital)
张三 已婚
李四 已婚
王五 未婚
赵六 离婚
孙七 未婚

 

表中有5行记录,所以创建的记录会有5位

性别列-男的位图: 10100

性别列-女的位图: 01011

 

已婚列的位图: 11000

未婚列的位图: 11000

离婚列的位图: 11000

 技术分享

所以现在知道了吧, 当要更新位图索引时,就要更新这个位图,那么将会锁定整个位图或者要更新的这个位所在的块。

以上是关于如何监控ORACLE索引使用与否的主要内容,如果未能解决你的问题,请参考以下文章

oracle 索引监控

如何查看oracle失效的索引

微软的mssql有没有象oracle一样的强制索引,如何使用?

oracle如何避免查询数据的时候索引失效

如何更好的使用Oracle全文索引

关于SQLServer 2000 索引问题