索引可能是我们对于数据库性能优化最常用的一个手段。这里简单说下里面的几个方面的问题。 1、 索引是一个对象; 索引是一个独立的数据库对象,和数据表table一样。在Oracle中,数据库对象object都是通过段segment结构表示。我们在数据字典dba_segment中,可以使用索引的名称搜索出与segment_name相等的字典项目。 SQL> select segment_name, segment_type, bytes, blocks, extents from dba_segments where segment_name='IND_T_OWNER_NAME'; SEGMENT_NAME SEGMENT_TYPE BYTES BLOCKS EXTENTS -------------------- --------------- ---------- ---------- ---------- IND_T_OWNER_NAME INDEX 3145728 384 18 上述就说明,该数据段对象IND_T_OWNER_NAME,对应的类型为索引。占据空间为3145728 bytes,约为3.16M的空间,对应384个数据块,包括在18个数据区中。 在堆表结构中,数据表和索引是可以分开进行存储的。通常,从性能角度考虑我们常将两者放置在不同的Tablespace中,这样做的目的主要是为了分散物理IO。 2、 索引是有代价的 索引的建立通常是为提高数据检索的效率。使用索引搜索的一个目的是避免全表扫描FTS,提高搜索效率。进行索引搜索时,实际上先进行一次索引结构读取,获取查询结果所在数据块的物理地址ROWID,之后进行第二次检索数据表块,直接获取到数据行信息。所以,在数据表较大,结果集较少的时候,通常进行索引检索的效率较好。反之,如果返回数据较多,例如返回整个大表的绝大部分记录行,这样往往是全表扫描的效果较好。 在现有的Oracle版本中,使用的CBO(基于成本的优化器)就是进行检索方案的选择确定。究竟是何种方案,要根据收集到的统计进行进行一系列的计算估算,最后确定执行计划。 当索引对象生效的时候,会实时保证和数据表的索引列同步。如果发生增加、修改和删除操作,索引也要进行相应的结构变化和修改,用来适应数据表结构的变化。所以,加入索引后,为了维护索引的完整性,增加、修改和删除等DML操作性能会受到一定程度的影响。 另一方面,作为一个独立的数据库对象,索引也是要消耗存储空间的。如果对应的数据表很大的话,相应索引的体积可以会达到一定程度。相应的,重建索引的效率也就成为不能忽视的一个问题。 总之,索引技术是一个使用空间和DML效率为交换,换高效检索的技术方案。 3、 索引的状态 从索引的数据字典上,我们可以看到数据索引的状态。 SQL> select index_name, index_type, status, leaf_blocks from dba_indexes where index_name='IND_T_OWNER_NAME'; INDEX_NAME INDEX_TYPE STATUS LEAF_BLOCKS ------------------------------ --------------------------- -------- ----------- IND_T_OWNER_NAME NORMAL VALID 287 索引的状态只有两个,VALID和UNUSABLE。Valid表示当前索引正在生效,会实时保证与数据表的一致性,如果发生DML操作,其内部结构会自动进行调整。一些带有约束特定的索引,如unique,也会起到数据完整性保持的作用。 另一个索引状态Unusable,表示当前索引停用。索引是不会和数据表联动的更新结构,也不会起到数据完整性保持的作用。 实际工作中,我们可以选择暂时禁用索引功能,来提高数据表插入、修改效率。因为,在索引起作用的情况下,大量数据的DML操作会带来很多的索引更新和Redo Log的生成。这在批量数据加载的时候是不需要的。所以,可以暂时禁用索引。 SQL> alter index ind_t_owner_name unusable; Index altered 查看索引状态: SQL> select index_name, index_type, status, leaf_blocks from dba_indexes where index_name='IND_T_OWNER_NAME'; INDEX_NAME INDEX_TYPE STATUS LEAF_BLOCKS ------------------------------ --------------------------- -------- ----------- IND_T_OWNER_NAME NORMAL UNUSABLE 287 Executed in 0.03 seconds 此时进行一系列的操作,是不会更新索引的。同时,一些操作,也不会走索引的执行计划。 SQL> select * from t where object_name='T' and wner='SYS'; 已用时间: 00: 00: 00.26 执行计划 ---------------------------------------------------------- Plan hash value: 1601196873 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 92 | 156 (2)| 00:00:02 | |* 1 | TABLE ACCESS FULL| T | 1 | 92 | 156 (2)| 00:00:02 | -------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("OBJECT_NAME"='T' AND "OWNER"='SYS') 统计信息 ---------------------------------------------------------- 386 recursive calls 0 db block gets 758 consistent gets 545 physical reads 116 redo size 1194 bytes sent via SQL*Net to client 385 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 5 sorts (memory) 0 sorts (disk) 1 rows processed 如果我们强制使用hint,要求执行索引路径时,是会出现错误提示的。 SQL> select /*+ index(t ind_t_owner_name) */* from t where object_name='T' and wner='SYS'; select /*+ index(t ind_t_owner_name) */* from t where object_name='T' and wner='SYS' ORA-01502: 索引 'SYS.IND_T_OWNER_NAME' 或这类索引的分区处于不可用状态 只有进行索引的重建rebuild,才可以实现索引状态的恢复和启用。 SQL> alter index ind_t_owner_name rebuild; Index altered Executed in 0.611 seconds SQL> select index_name, index_type, status, leaf_blocks from dba_indexes where index_name='IND_T_OWNER_NAME'; INDEX_NAME INDEX_TYPE STATUS LEAF_BLOCKS ------------------------------ --------------------------- -------- ----------- IND_T_OWNER_NAME NORMAL VALID 287 Executed in 0.06 seconds SQL> select /*+ index(t ind_t_owner_name) */ object_name from t where object_name='T' and wner='SYS'; OBJECT_NAME -------------------------------------------------------------------------------- T Executed in 0.02 seconds 默认也会将索引考虑入执行计划: SQL> select * from t where object_name='T' and wner='SYS' ; 已用时间: 00: 00: 00.01 执行计划 ---------------------------------------------------------- Plan hash value: 1404465244 ------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 92 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 92 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IND_T_OWNER_NAME | 1 | | 1 (0)| 00:00:01 | ---------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("OWNER"='SYS' AND "OBJECT_NAME"='T') 统计信息 ---------------------------------------------------------- 1 recursive calls 0 db block gets 4 consistent gets 0 physical reads 0 redo size 1198 bytes sent via SQL*Net to client 385 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed 有一点额外的也要注意,如果使用的是唯一索引unique类型,在从unusable转变为valid的过程中,要进行数据列的一个检查。如果发现列的值已经不满足唯一性条件,则报错,并且索引状态不会发生变化。 除了手工进行索引的状态变化之外,一些管理操作,如move、分区表操作,也会影响到索引的状态。实际中可能需要进行rebuild工作,对一些比较大的数据表,rebuild工作的时间可能也会比较长。 4、 索引的监控 无论是投产之后还是开发测试中,我们都在数据表中加入了一些索引。通常我们是不能实时监视每条语句的执行计划,那么有没有一些手段可以监控索引的执行情况,发现一些不常用的索引,定位优化目标呢? 答案是肯定的。在oracle中,可以借助monitoring usage关键字和v$object_usage视图实现这个功能。 启用监控功能并且收集监控结果。 SQL> alter index ind_t_owner_name monitoring usage; Index altered SQL> select /*+ index(t ind_t_owner_name) */ object_name from t where object_name='T' and wner='SYS'; OBJECT_NAME ----------------------------------------- T Executed in 0.01 seconds SQL> select * from v$object_usage where index_name='IND_T_OWNER_NAME'; INDEX_NAME TABLE_NAME MONITORING USED START_MONITORING END_MONITORING -------------------- ---------- ---------- ---- ------------------- ------------------- IND_T_OWNER_NAME T YES YES 12/07/2010 23:33:36 Executed in 0.01 seconds 收集所有的相关视图信息,可以方便的找出哪个Index是一直没有使用过的,也就可以进一步定位到相应功能和SQL语句。 关闭监控功能: SQL> alter index ind_t_owner_name nomonitoring usage; Index altered Executed in 0.01 seconds 5、 索引的管理 在最近看的一本书中,提出了一个比较新的索引管理思路,觉得值得借鉴。 我们建索引是一项有代价的工作,牺牲DML操作来实现索引的同步。那么,我们如果确定加什么索引,什么时候加索引。本质上还要看系统怎么使用数据表,更进一步是传入的SQL结构是一个什么样子,根据这些信息进行索引的管理。 同时,在DBA的工作中,要加入索引信息维护追踪的机制。那个索引对应那个模块的那个需求而建立,当这个需求变化或者弱化后,索引要随之发生变化。不能残留很多各种原因建立的索引。这样是给SQL执行计划带来很多问题。在CBO时代,积极的更新统计信息大部分时候要比强制用hint有效的多。 上述是几点拙见,忘路过之高人不吝指教。 (责任编辑:IT) |