【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://blog.csdn.net/m0_69908381/article/details/131094864
出自【进步*于辰的博客】
无论使用的是
oracle
、mysql
,亦或者其他数据库,几乎所有企业级项目都会使用索引,因为这能大大提升程序性能。
oracle
索引如何实现性能优化?这就是本文阐述的核心。
参考笔记一,P25.27、P26.28、P30.2;笔记二,P70/71。
注:“索引值”指索引原子(即
B+树
中的一个节点);“索引列值”指记录/数据。
文章目录
- 1、概述
- 1.1 创建
- 1.2 查询
- 1.3 适用场景
- 1.4 不适用场景
- 1.5 补充说明
- 2、关于索引构建过程(性能优化原理)
- 2.1 构建过程
- 2.2 补充说明
- 2.3 索引结构细节
- 3、关于索引分类
- 3.1 唯一索引
- 3.2 组合索引
- 3.3 反向键索引
- 3.4 位图索引
- 3.5 基于函数的索引
- 4、最后
1、概述
下图红框处是数据表的rowid
。
其是每行记录的唯一标识。
从宏观上说,索引是排序后的rowid
,其排序不是指升序或降序,而是将rowid
按照一定的数据结构进行排列。
先说结论:
rowid
本身无任何优化查询的功能,索引可以实现优化依赖于数据结构。
1.1 创建
- 方式1:当添加主键或唯一键时,会自动生成索引;
- 方式2:
create index 索引名 on 表名(字段名)
。
1.2 查询
user_indexes
:查询用户所有索引;user_ind_columns
:查询用户所有索引对应的字段。
1.3 适用场景
- 数据量大;
- 结果集所包含记录量占数据表记录量的
2% ~ 4%
左右; - 经常用作条件或多表连接的字段;
- 所查询字段的记录范围很广;
- 所查询字段包含大量 null,因为索引不包含 null。
1.4 不适用场景
- 数据量小;
- 不常作为条件的字段;
- 频繁更新的字段;
- 索引字段是条件的一部分时。如:
where emp.salary * 2 > 10000
,此时索引无效;(原因后续说明) - 条件中包含单行函数时。如:
where round(salary) > 10000
。(除“基于函数的索引”外,见下文)
1.5 补充说明
- 优点:优化查询速度。
- 不足:占用内存;索引数量多时难以维护;降低 DML 时性能。
- 适用场景:当数据量大、不经常进行 DML 且访问频率高时。(对上述阐述的总结)
- 一般情况下,先添加索引,后新增记录;(原因后续说明)
- 当条件中包含
like
、in
、not in
等范围查询时,索引失效。(下文说明)
2、关于索引构建过程(性能优化原理)
启发源自博文【Oracle索引结构】(转发)。
2.1 构建过程
注:
1、下文阐述中,id
为数据表其中一个字段,非主键(上文阐述:“在创建主键或唯一键时,会自动创建索引”,这种索引称之为主键索引,即索引建立在主键字段上。实际上,可以在任何某个或某多个字段上建立索引,并且索引有多种,看下文);
2、上文阐述:“索引所实现的性能优化依赖于所使用的数据结构”。无论是oracle
、mysql
,索引的数据结构都包括B+树
、hashtable
这2种。下述以B+树
这种数据结构作为阐述示例,B+树
也是索引所使用的主要数据结构之一。
借用那篇博文中的2张图,便于大家理解。
- 将
rowid
和id
取出,以类似entry
的结构组成一个,即一条记录对应一个。然后几个组成一块(叶节点);
注:这个即索引值,不过其不是entry
,下文简述索引细节。这里说的entry
,就是Map.Entry
。 - 几个块由某个块(茎节点)管理,此节点记录着所管理的多个块的信息,即索引;
- 。。。以此类推。。。
- 由某个节点(根节点)管理所有的茎节点。
总结:
这种“B+树
型索引,所有的记录都存放在“叶节点”中,而“茎节点”和“根节点”存放的都是所管理节点的信息,包括“索引列值”和rowid
,即索引,也称之为“索引值”(见序言)。
2.2 补充说明
B+树
高度较低。如:在一个有几百亿条记录的数据表上建立索引,“树”高度不过 20 余层;- 数据库最小单位是块,最小分配单位是区,存放一个段至少需要一个区;(最后这点我目前也不明其意)
- 索引查找不一定比全表扫描(无索引)效率高的原因:
全表扫描一次可读多个块,而索引查找一次只能读一个。而索引查找的记录可能分散分布于多个块,即索引查找的读取次数可能较多于全局扫描。上文中第1.5项的第5点就是这种情况,因此致使索引失效;
注:索引采用B+树
,这种结构可大大加快检索速度,不过,若是上面这种情况,索引查找就需要多次通过IO
流读取各个块,自然效率大打折扣。有很多种方法可应对这种情况,其中之一是“优化B+树
”。因为B+树
是没有“闭环”的(所谓“闭环”,即单分支,没有回路)。“优化B+树
”的大致实现就是将所有“叶节点”相连,构成链表,即形成回路,从而减少读取IO
流的次数。(关于“优化B+树
”,后续详述) - 索引的数据结构是
B+树
,本身有序。
2.3 索引结构细节
大家看另1张图。
可见,索引值由Index entry header
、Key column length
、Key column value
(列值,即上文中的“索引列值”)、ROWID
组成。
3、关于索引分类
3.1 唯一索引
主键或唯一键创建时自动生成;
手动创建:create unique index 索引名 on 表名(字段名)
。
3.2 组合索引
当创建索引时,指定多列,就是组合索引。其中,根据上文【构建过程】,可知组合索引的每一个索引列值都包含组合列的各个数据。
注意: 只有当条件(where
)中包含此组合所有或大部分字段时,索引才能生效(后续详述)。
3.3 反向键索引
反转索引列值的每个字节,从而实现索引值的均匀分配,避免B+树
不平衡。(注:“不平衡”是指某个/某些分支较其他同高度分支较长,致使分支“倾斜”的情况)
常用宇连续增长、且索引列值前段相同的字段。如:索引列值为1001
、 1002
、1003
,经反向键索引转化后变为1001
、 2001
、3001
。
手动创建:create index 索引名 on 表名(字段名) reverse
。
3.4 位图索引
适合创建于“低基数”的字段,如:性别
、国家编号
。(注:“低基数”指取值固定或取值范围很小、且不经常更新)
上文阐述:“索引值由索引列值(记录/数据)和rowid
组成”,而位图索引不是。
位图索引不直接存储rowid
,而是存储字节位与rowid
的映射,目的是减少空间占用。
因此,位图索引适用于数据仓库,不适用于OLTP
(关于OLTP
,后续说明)。
手动创建:create bitmap index 索引名 on 表名(字段名)
。
3.5 基于函数的索引
指基于1/n
个字段上的函数或表达式创建的索引。简言之,在创建此类索引时,使用了表达式或函数。
限制: 表达式中不能使用聚合函数(如:count、max、sum
),比较常用的是单行函数;字段类型不能是lob(clob、blob)
;且当前用户必须拥有query rewrite
权限。
示例:
需求:查询用户名,用“基于函数的索引”进行优化。
语句:... where upper(user_name) = ‘xx’ ...
。
手动创建:create index 索引名 on 表名(upper(字段名))
。
4、最后
本文是“纯阐述”,可谓是0
示例,这样难免缺乏可信性和可行性。因此,本文的目的是为了让大家对oracle
索引所实现的性能优化原理、以及分类有一个大致的了解。
本文完结。