首页 > 代码库 > oracle 优化——索引与组合索引
oracle 优化——索引与组合索引
1、索引结构。第一张图是索引的官方图解,右侧是存储方式的图解。
图中很清晰的展示了索引存储的状况。
在leaf 节点中存储了一列,索引所对应项的 :值,rowId,长度,头信息(控制信息)
这样我们就能很清楚、如果通过索引查找数据,而只需要这个索引的值的时候,写上列名,就可以不需要回表。
2、索引在一般的数据量情况下,只有三层。leaf 是目录,branch 是目录的目录。可以做一个测试
1 drop table t1 purge; 2 drop table t2 purge; 3 drop table t3 purge; 4 drop table t4 purge; 5 drop table t5 purge; 6 drop table t6 purge; 7 drop table t7 purge; 8 9 10 create table t1 as select rownum as id ,rownum+1 as id2,rpad(‘*‘,1000,‘*‘) as contents from dual connect by level<=1;11 create table t2 as select rownum as id ,rownum+1 as id2,rpad(‘*‘,1000,‘*‘) as contents from dual connect by level<=10;12 create table t3 as select rownum as id ,rownum+1 as id2,rpad(‘*‘,1000,‘*‘) as contents from dual connect by level<=100;13 create table t4 as select rownum as id ,rownum+1 as id2,rpad(‘*‘,1000,‘*‘) as contents from dual connect by level<=1000;14 create table t5 as select rownum as id ,rownum+1 as id2,rpad(‘*‘,1000,‘*‘) as contents from dual connect by level<=10000;15 create table t6 as select rownum as id ,rownum+1 as id2,rpad(‘*‘,1000,‘*‘) as contents from dual connect by level<=100000;16 create table t7 as select rownum as id ,rownum+1 as id2,rpad(‘*‘,1000,‘*‘) as contents from dual connect by level<=1000000;17 18 19 create index idx_id_t1 on t1(id);20 create index idx_id_t2 on t2(id);21 create index idx_id_t3 on t3(id);22 create index idx_id_t4 on t4(id);23 create index idx_id_t5 on t5(id);24 create index idx_id_t6 on t6(id);25 create index idx_id_t7 on t7(id);26 27 set linesize 100028 set autotrace off29 select index_name,30 blevel,31 leaf_blocks,32 num_rows,33 distinct_keys,34 clustering_factor35 from user_ind_statistics36 where table_name in( ‘T1‘,‘T2‘,‘T3‘,‘T4‘,‘T5‘,‘T6‘,‘T7‘);37 38 索引的名字 层级 leaf 块39 INDEX_NAME BLEVEL LEAF_BLOCKS NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR40 ------------------ ----------- ---------- ------------- -----------------41 IDX_ID_T1 0 1 1 1 142 IDX_ID_T2 0 1 10 10 243 IDX_ID_T3 0 1 100 100 1544 IDX_ID_T4 1 3 1000 1000 14345 IDX_ID_T5 1 21 10000 10000 142946 IDX_ID_T6 1 222 100000 100000 1428647 IDX_ID_T7 2 2226 1000000 1000000 142858
数据在一千万条的时候也只有三层(查到数据只需要四个逻辑读),所以即便千亿数据,也无非十个左右的逻辑读,充分了体现了索引的优点。如果不建索引,那么全表扫描是很恐怖的。
3、索引在函数运算上能起到优化作用,比如sum,avg,count
在没有主键,只有索引的表中count*的时候,由于索引不记录空值,所以它不会走索引,如果对索引列加上 not null“select count(*) from user where userId not null”,就能走索引、当然如果有主键自然就不必了。
各种函数,运算列如果是索引列的话效率就会大幅度提高,可以看下面例子
1 SUM/AVG的优化 2 drop table t purge; 3 create table t as select * from dba_objects; 4 create index idx1_object_id on t(object_id); 5 set autotrace on 6 set linesize 1000 7 set timing on 8 9 select sum(object_id) from t; 10 执行计划11 ----------------------------------------------------------------------------------------12 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |13 ----------------------------------------------------------------------------------------14 | 0 | SELECT STATEMENT | | 1 | 13 | 49 (0)| 00:00:01 |15 | 1 | SORT AGGREGATE | | 1 | 13 | | |16 | 2 | INDEX FAST FULL SCAN| IDX1_OBJECT_ID | 92407 | 1173K| 49 (0)| 00:00:01 |17 ----------------------------------------------------------------------------------------18 统计信息19 ----------------------------------------------------------20 0 recursive calls21 0 db block gets22 170 consistent gets23 0 physical reads24 0 redo size25 432 bytes sent via SQL*Net to client26 415 bytes received via SQL*Net from client27 2 SQL*Net roundtrips to/from client28 0 sorts (memory)29 0 sorts (disk)30 1 rows processed31 32 --比较一下假如不走索引的代价,体会一下这个索引的重要性33 select /*+full(t)*/ sum(object_id) from t; 34 SUM(OBJECT_ID)35 --------------36 2732093100 37 执行计划38 ---------------------------------------------------------------------------39 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |40 ---------------------------------------------------------------------------41 | 0 | SELECT STATEMENT | | 1 | 13 | 292 (1)| 00:00:04 |42 | 1 | SORT AGGREGATE | | 1 | 13 | | |43 | 2 | TABLE ACCESS FULL| T | 92407 | 1173K| 292 (1)| 00:00:04 |44 ---------------------------------------------------------------------------45 统计信息46 ----------------------------------------------------------47 0 recursive calls48 0 db block gets49 1047 consistent gets50 0 physical reads51 0 redo size52 432 bytes sent via SQL*Net to client53 415 bytes received via SQL*Net from client54 2 SQL*Net roundtrips to/from client55 0 sorts (memory)56 0 sorts (disk)57 1 rows processed 58 59 --起来类似的比如AVG,和SUM是一样的,如下:60 select avg(object_id) from t; 61 AVG(OBJECT_ID)62 --------------63 37365.533864 执行计划65 ----------------------------------------------------------------------------------------66 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |67 ----------------------------------------------------------------------------------------68 | 0 | SELECT STATEMENT | | 1 | 13 | 49 (0)| 00:00:01 |69 | 1 | SORT AGGREGATE | | 1 | 13 | | |70 | 2 | INDEX FAST FULL SCAN| IDX1_OBJECT_ID | 92407 | 1173K| 49 (0)| 00:00:01 |71 ----------------------------------------------------------------------------------------72 统计信息73 ----------------------------------------------------------74 0 recursive calls75 0 db block gets76 170 consistent gets77 0 physical reads78 0 redo size79 448 bytes sent via SQL*Net to client80 415 bytes received via SQL*Net from client81 2 SQL*Net roundtrips to/from client82 0 sorts (memory)83 0 sorts (disk)84 1 rows processed85 86 --不知大家注意到没,这里的试验已经告诉我们了,OBJECT_ID列是否为空,也不影响SUM/AVG等聚合的结果。
可以从执行计划中看到 consistent gets 走索引是 一百多, 而不走索引就上千,效率成倍增加。
4、索引本身有序
通过索引列排序,不会产生排序 ,但是io多,代价小。排序代价是很大的。 由于本身有序,获取最大值,最小值,也是高效的。
二、组合索引。(索引只适合返回少量记录)
1、适用在单独查询返回记录很多,组合查询后忽然返回记录很少的情况
比如:1某地区的贷款条数很多,2涉农贷款条数很多,3某地区贷款金额在某区间的贷款数很多
但是,某地区,涉农贷款,金额某区间的 贷款条数 就很少。
当涉及某些组合条件检索数据时,就可以采用组合索引的形式,
create index area_loanType_ on t(area_id,loan_type);
而时间是做了分区表的,所以在这种情况下、检索速度会大大提高。
2.组合索引第一个索引的重要性。
由于组合索引是仅仅相对于前一个索引有意义,所以,当查询时,只通过第二个索引列查询,是不会走索引的,它的存在的意义只能是在使用第一个索引的情况下。
但是,只通过第一个索引列 查询、是会走索引的。所以第一个索引也是常用,并且有单独查寻需要的列。
当然如果前一个索引的值很少,比如loanType(涉农,个体,工业,慈善..) 共六七个而已,那么Oracle 收集了统计信息后,可以索引跳跃扫描,类似与 SELECT * from loans where areaId="" and loanType in (慈善,个体,工业...)and(第三个组合索引 的条件)
3.仅等值无范围查询时,组合索引顺序不影响性能
比如:
1 --3.仅等值无范围查询时,组合索引顺序不影响性能(比如where col1=xxx and col2=xxx,无论COL1+COL2组合还是COL2+COL1组合) 2 3 drop table t purge; 4 create table t as select * from dba_objects; 5 insert into t select * from t; 6 insert into t select * from t; 7 insert into t select * from t; 8 update t set object_id=rownum ; 9 commit;10 create index idx_id_type on t(object_id,object_type);11 create index idx_type_id on t(object_type,object_id);12 set autotrace off13 alter session set statistics_level=all ;14 set linesize 36615 16 select /*+index(t,idx_id_type)*/ * from t where object_id=20 and object_type=‘TABLE‘;17 select * from table(dbms_xplan.display_cursor(null,null,‘allstats last‘));18 -----------------------------------------------------------------------------------------------------19 | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |20 -----------------------------------------------------------------------------------------------------21 | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 5 |22 | 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 57 | 1 |00:00:00.01 | 5 |23 |* 2 | INDEX RANGE SCAN | IDX_ID_TYPE | 1 | 9 | 1 |00:00:00.01 | 4 |24 -----------------------------------------------------------------------------------------------------25 26 select /*+index(t,idx_type_id)*/ * from t where object_id=20 and object_type=‘TABLE‘;27 select * from table(dbms_xplan.display_cursor(null,null,‘allstats last‘));28 Plan hash value: 342076862829 30 -----------------------------------------------------------------------------------------------------31 | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |32 -----------------------------------------------------------------------------------------------------33 | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 5 |34 | 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 57 | 1 |00:00:00.01 | 5 |35 |* 2 | INDEX RANGE SCAN | IDX_TYPE_ID | 1 | 9 | 1 |00:00:00.01 | 4 |36 -----------------------------------------------------------------------------------------------------37 38 39 --4.组合索引最佳顺序一般是将列等值查询的列置前。(测试组合索引在条件是不等的情况下的情况,条件经常是不等的,要放在后面,让等值的在前面)40 41 select /*+index(t,idx_id_type)*/ * from t where object_id>=20 and object_id<2000 and object_type=‘TABLE‘;42 select * from table(dbms_xplan.display_cursor(null,null,‘allstats last‘));43 -----------------------------------------------------------------------------------------------------44 | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |45 -----------------------------------------------------------------------------------------------------46 | 0 | SELECT STATEMENT | | 1 | | 469 |00:00:00.01 | 86 |47 | 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 14 | 469 |00:00:00.01 | 86 |48 |* 2 | INDEX RANGE SCAN | IDX_ID_TYPE | 1 | 1 | 469 |00:00:00.01 | 40 |49 -----------------------------------------------------------------------------------------------------50 51 52 53 54 select /*+index(t,idx_type_id)*/ * from t where object_id>=20 and object_id<2000 and object_type=‘TABLE‘;55 -----------------------------------------------------------------------------------------------------56 | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |57 -----------------------------------------------------------------------------------------------------58 | 0 | SELECT STATEMENT | | 1 | | 469 |00:00:00.01 | 81 |59 | 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 469 | 469 |00:00:00.01 | 81 |60 |* 2 | INDEX RANGE SCAN | IDX_TYPE_ID | 1 | 469 | 469 |00:00:00.01 | 35 |61 -----------------------------------------------------------------------------------------------------
4、组合索引排序 如果对组合索引中列进行排序,可以走索引。
三、创建索引的一些规则
1、权衡索引个数与DML之间关系,DML也就是插入、删除数据操作。
这里需要权衡一个问题,建立索引的目的是为了提高查询效率的,但建立的索引过多,会影响插入、删除数据的速度,因为我们修改的表数据,索引也要跟着修改。这里需要权衡我们的操作是查询多还是修改多。
2、把索引与对应的表放在不同的表空间。
当读取一个表时表与索引是同时进行的。如果表与索引和在一个表空间里就会产生资源竞争,放在两个表这空就可并行执行。
3、最好使用一样大小是块。
Oracle默认五块,读一次I/O,如果你定义6个块或10个块都需要读取两次I/O。最好是5的整数倍更能提高效率。
4、如果一个表很大,建立索引的时间很长,因为建立索引也会产生大量的redo信息,所以在创建索引时可以设置不产生或少产生redo信息。只要表数据存在,索引失败了大不了再建,所以可以不需要产生redo信息。
oracle 优化——索引与组合索引