您的位置:首页 > 数据库 > Oracle

Oracle 优化——位图、函数等索引介绍

2017-10-10 14:42 435 查看
原文:http://www.cnblogs.com/javaMan/p/4012355.html


一、位图索引

我将使用一个例子,来描述位图索引的存储,并分析它的优点。

Table :Loans 放贷信息

 
IDuserId行业投向币种证件类型还本付息方式状态
11农业人民币身份证等额本息还款法已上报
22农业人民币身份证等本还款法未上报
31工业人民币护照按季计息到期还本法已上报
42个体人民币身份证等本还款法已上报
55其他人民币身份证按月计息到期还本法未上报
 

 

 


 

我对行业投向,和还本付息方式添加了位图索引

create bitmap index index_投向 on loans(行业投向);

create bitmap index index_还本付息方式 on loans(还本付息方式);


那么它会这么对位图索引进行存储:当前列的每一种值,存放在一个块中,通过0和1来标示改rownumber是否存在改值。

行业投向位图索引/还本付息方式  
值/行第一行第二行第三行第四行
农业 1 1 0 0
工业 0 0 1 0
个体 0 0 0 1
其他 0 0 0 0
值/行第一行第二行第三行第四行
等额本息还款法 1 0 0 0
等本还款法 0 1 0 0
按季计息到期还本法 0 0 1 0
按月计息到期还本法 0 0 01
有图可以看出, 农业、工业、个体都各以一个块来存放 所有列“自己是否为真”。 

所以暂时可以得出:

1、位图索引,必须创建在“仅仅几种值的情况”。 

  如果在低重复度的列上创建位图索引是很恐怖的,他将创建N多个块来存储。不论创建,还是查询,都是不聪明的。

 

2、位图索引,不适合放在常修改的字段列(如状态列)容易发生死锁。   

  位图索引死锁情况举例

--SESSION 1(持有者)
DELETE FROM LOANS WHERE 行业投向='农业' AND status=1;

---SESSION 2(其他会话) 插入带'农业'的记录就立即被阻挡,以下三条语句都会被阻止
insert into loans(Id,投向.....) values (1,'农业',....);
update t set 投向='工业' WHERE id=25;
delete from loans WHERE 行业投向='农业';

--以下是可以进行不受阻碍的
insert into loans(Id,投向.....) values (1,'工业',....);
delete from t where gender='工业' ;
UPDATE T SET status='aa' WHERE ROWID NOT IN ( SELECT ROWID FROM T WHERE 投向='工业' ) ;

--update只要不更新位图索引所在的列即可


 

3、索引通过 比特位 存储01,来标示真假,占用内存很小,检索效率极高。 

  count(*) where 行业投向 = 农业,效率是很高的, 

当采集平台完成这些金融数据采集后,金融监管部门要对信息进行分析、统计,形成报表。有位图索引效率是很好的。

具体案例



1 /*
2 总结:本质原因:其实就是位图索引存放的是0,1的比特位,占字节数特别少。
3 */
4
5 --位图索引跟踪前准备
6 drop table t purge;
7 set autotrace off
8 create table t as select * from dba_objects;
9 insert into t select * from t;
10 insert into t select * from t;
11 insert into t select * from t;
12 insert into t select * from t;
13 insert into t select * from t;
14 insert into t select * from t;
15 update t set object_id=rownum;
16 commit;
17
18 --观察COUNT(*)全表扫描的代价
19 set autotrace on
20 set linesize 1000
21 select count(*) from t;
22
23
24
25 ------------------------------------------
26   COUNT(*)
27 ----------
28   4684992
29 执行计划
30 ----------------------------------------------------------
31 Plan hash value: 2966233522
32
33 -------------------------------------------------------------------
34 | Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |
35 -------------------------------------------------------------------
36 |   0 | SELECT STATEMENT   |      |     1 | 20420  (11)| 00:04:06 |
37 |   1 |  SORT AGGREGATE    |      |     1 |            |          |
38 |   2 |   TABLE ACCESS FULL| T    |   294M| 20420  (11)| 00:04:06 |
39 -------------------------------------------------------------------
40 统计信息
41 ----------------------------------------------------------
42           0  recursive calls
43           0  db block gets
44       66731  consistent gets
45           0  physical reads
46           0  redo size
47         426  bytes sent via SQL*Net to client
48         415  bytes received via SQL*Net from client
49           2  SQL*Net roundtrips to/from client
50           0  sorts (memory)
51           0  sorts (disk)
52           1  rows processed
53
54
55
56
57
58 --观察COUNT(*)用普通索引的代价
59 create index idx_t_obj on t(object_id);
60 alter table T modify object_id not null;
61 set autotrace on
62 select count(*) from t;
63
64
65
66
67
68   COUNT(*)
69 ----------
70   4684992
71 普通索引的执行计划
72 ---------------------------------------------------------------------------
73 | Id  | Operation             | Name      | Rows  | Cost (%CPU)| Time     |
74 ---------------------------------------------------------------------------
75 |   0 | SELECT STATEMENT      |           |     1 |  3047   (2)| 00:00:37 |
76 |   1 |  SORT AGGREGATE       |           |     1 |            |          |
77 |   2 |   INDEX FAST FULL SCAN| IDX_T_OBJ |  4620K|  3047   (2)| 00:00:37 |
78 ---------------------------------------------------------------------------
79 普通索引的统计信息
80 ----------------------------------------------------------
81           0  recursive calls
82           0  db block gets
83       10998  consistent gets
84           0  physical reads
85           0  redo size
86         426  bytes sent via SQL*Net to client
87         415  bytes received via SQL*Net from client
88           2  SQL*Net roundtrips to/from client
89           0  sorts (memory)
90           0  sorts (disk)
91           1  rows processed
92
93
94
95
96 --观察COUNT(*)用位图索引的代价(注意,这里我们特意取了status这个重复度很高的列做索引)
97 create bitmap index idx_bitm_t_status on t(status);
98 select count(*) from t;
99
100 SQL> select count(*) from t;
101
102
103
104
105
106
107   COUNT(*)
108 ----------
109   4684992
110
111 位图索引的执行计划
112 -------------------------------------------------------------------------------------------
113 | Id  | Operation                     | Name              | Rows  | Cost (%CPU)| Time     |
114 -------------------------------------------------------------------------------------------
115 |   0 | SELECT STATEMENT              |                   |     1 |   115   (0)| 00:00:02 |
116 |   1 |  SORT AGGREGATE               |                   |     1 |            |          |
117 |   2 |   BITMAP CONVERSION COUNT     |                   |  4620K|   115   (0)| 00:00:02 |
118 |   3 |    BITMAP INDEX FAST FULL SCAN| IDX_BITM_T_STATUS |       |            |          |
119 -------------------------------------------------------------------------------------------
120 位图索引的统计信息
121 ----------------------------------------------------------
122           0  recursive calls
123           0  db block gets
124         125  consistent gets
125           0  physical reads
126           0  redo size
127         426  bytes sent via SQL*Net to client
128         415  bytes received via SQL*Net from client
129           2  SQL*Net roundtrips to/from client
130           0  sorts (memory)
131           0  sorts (disk)
132           1  rows processed
133
134


 

 


二、反向索引

 假如 我现在有些ID  100001,100002,100003,100004,100005 ,那么反向索引 ,他的索引创建的就是  100001,200001,300001,400001,500001。 由于序列本身有序,会根据范围放在不同的叶子块中

详见:索引,组合索引篇      那么索引就被放在不同的快中,有效的减少了热快争用。



再看一下这张图, 最下面就是叶子块  ,100001 和200001 和300001 会放在不同的块中,而一般常常会频繁的访问近期的数据,那么由于他们在不同的块中,在索引进行检索的时候,能够有效的减少资源竞争。

创建反向索引的sql

---反向索引
create index rev_index on t(column) reverse;

---将反向索引转换成普通索引。
alter index rev_index  rebuild noreverse;


2、反向索引,在进行范围查询的时候无效,  

3、反向索引无序了,所以无法走索引排序,

 


三、函数索引

我们现在,有一个场景:有一列数据是有大小写的,但是查询的时候,不需要区分大小写。

那么语句只能这么写    select * from t  where upper(object_name)='T' ;

 

首先有一个常识,就是  走了函数查询,不会走索引。 就像有些查询 列的类型与值类型不匹,会进行值类型函数转换,然后无法进行索引查询。

eg: id为varchar类型  而查询语句为:select * from t where id = 1。  由于数据字段为varchar类型,而参数为number 类型,故会进行值类型转换。检索就走了全表扫描。

 

那么如何实现场景需求呢? 只能让函数索引一展身手:

create index idx_func_ojbnam on t( upper(object_name) );


upper()是Oracle内部函数

现在   select * from t  where upper(object_name)='T' ;   这条语句就能走上索引。

请看具体案例


 场景中使用函数索引的案例实战(可以直接运行)


 列类型不匹配导致无法走索引的案例实战

 

2、自定义函数索引

首先创建一个自定义函数让 id-1 的形式创建序列,当然没什么意义。

create or replace function f_minus1(i int)
return int DETERMINISTIC
is
begin
return(i-1);
end;


create index idx_test on test (f_minus1(object_id));


DETERMINISTIC关键字很重要
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: