对象:表、索引、存储过程、函数都称为对象
Schema:某个用户下面对象的集合
Schema的名称与用户名相同,但是schema与用户不是一回事,如果用户下面没有任何对象,那么这个用户就不存在schema,用户下面有对象了,那么schema就存在了
Oracle数据库里面最重要的对象就是表和索引
表用来存储数据
索引用来加快查询速度
表
表名+字段名+字段类型
每个表都会对应一个segment,表物理上存在segment里面
表的作用在于方便我们更新对应segment里的数据,以及将segment里的数据格式化成我们容易理解的形式,并展现出来
Oracle里面表的分类
1、普通表
一个表对应一个segment
2、分区表
一个表被分成多个分区,每个分区对应一个segment,每个segment可以位于不同的表空间里,对于数据量非常大的表,我们可以采用这种方式。
3、索引组织表IOT
IOT必须有一个主键索引,同时所有的数据与主键列一起存放在主键索引的叶子节点里,IOT在存储上属于索引,因此通过索引来对应segment
4、簇表cluster table
应用场合比较明显
主表和明细表,经常两个表进行关连查询,例如订单主表、订单明细表
通过建立簇表,一个数据块里面存放了两个表的数据,加快了关联查询速度
普通表
Oracle为列提供了很多的类型
char(n):字符长度为n,不足部分以空格补齐
varchar2(n):变化长度的字符串,根据实际占用分配空间
number(n, m):n表示数据的总长度,m表示小数位数
date:日期类型,包含日期和时分秒
等等,上面是常用的
使用OEM创建表
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937382lA0B.png"></a>
表的具体定义选项
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937382FiGi.png"></a>
存储相关信息
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_13359373828f3Q.png"></a>
表空间:这个表对与DML语句是否启用日志
区数:表的初始区的大小,默认值就是表所在的表空间上所设定的区大小
空间使用情况:数据块在剩余空间&gt;10%的时候,可以继续insert,当小于10%的时候,不能继续insert,剩余空间留给update
事务处理数:该表的数据块头部ITL槽的个数和最大个数
缓冲池:表属于哪个缓冲池
因为表空间启用了ASSM,因此没有参数pctused
pctused表示当数据块的剩余。40%的时候,数据块可以重新insert
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937383w82A.png"></a>
这个表的创建SQL
数据行在数据块里的结构
数据行头部——列长度——列值
1、数据行的头部
每一个数据行都有一个行头部,在这里存放了该行数据所包含的列的数量、锁定标记
当某个事务更新某条记录的时候,会在该数据行的头部记录所使用的ITL槽号以及锁定标记
2、列的长度和列的值,每个列之间没有空隙,都是紧密排列
3、不同的数据行之间也没有空隙,紧密排列
数据块的头部有一个结构:行目录,在行目录中,为每条数据行都记录了一个条目,每个条目就指向该记录的行头部,所以Oracle能够区分不同的行
每一条记录都有一个ROWID列,这是一个伪列。该列的值并没有实际的保存在数据块里面,但是可以显示和查询。
Oracle 10g中,rowid列的格式是
OOOOOOFFFBBBBBBRRR
OOOOOO表示该数据行所在的对象名,一般都是表名
FFF表示该数据行所在的相对文件号
BBBBBB表示该数据行所在的数据块号
RRR表示该行在数据块中的行号
ROWID采用的是64进制
A-Z 0-25
a-z 26-51
0-9 52-61
/ 62
+ 63
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937383JUrv.png"></a>
对象号:0+0+0+2*64^2+44*64^1+61*64^0=11069
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937384rWmI.png"></a>
文件号:0+0+4*64^0=4
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937384OOc2.png"></a>
数据块号:0+0+0+0+7*64^1+29*64^0=477
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937385jruU.png"></a>
行号:0+0+0
管理表的主要工作
扩展表
1、主动地扩展一个表所占用的空间(就是给表分配一个extent)
2、将一个表分配到多个数据文件上,实现IO均衡
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937385xxQ1.png"></a>
主动的给表分配一个extent,在当前的数据文件里分配。
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937385QRla.png"></a>
将表分配到指定的文件上,实现负载均衡。
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937386yIEe.png"></a>
重整表
将一个表从目前的表空间转移到另外一个表空间中去,或者消除表的数据块级别的碎片
数据块级别的碎片指的是每个数据块里含有的数据行太少了,例如100条记录分布在100个数据块中,这种表称为稀疏表
稀疏表产生的原因
该表上存在很多的insert、delete
表的segment header里记录了一个值,叫高水位标记(HWM high water mark)
HWM表示当前segment里使用的最后一个数据块的位置,当发生insert时,extent不断的分配,HWM不断的增长
HWM最典型的好处就是select count(*)的时候,引起表扫描,服务器进程在扫描数据块的时候,只扫描到HWM为止,因为HWM后面不会有segment的数据块
当delete发生的时候,HWM不会下降的,即使表里面的数据全部删除,HWM也不会下降,这势必会影响select count(*)的性能
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_13359373865o8t.png"></a>
这个表里面的数据排列不够紧密,我们需要重新整理一下数据行在数据块里的分布,使其分布的紧密一些。即节省了空间,又提高了select的速度
10g以前,我们只能使用move或者导入导出的方式对表进行重整,达到降低HWM的目的,10g以后可以使用shrink对表进行收缩
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937387y3uN.png"></a>
这个表占用了52个数据块。删除最后的五千行数据以后,还是占用了52个数据块。
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_13359373878YCw.png"></a>
数据表迁移到另外一个表空间以后,重新进行了整理。
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937387KjCo.png"></a>
表占用的数据块明显减少。
注意:表进行move以后,表上的所有索引失效,需要重建。
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937388HQ5x.png"></a>
可以为了碎片整理,减低HWM,在表空间内部进行move。
对于消除数据块级别的碎片来说,Oracle10g之前采用了move、exp/imp的方式进行解决。当时都会停止应用。
Oracle 10g开始采用了shrink技术
Shrink是通过事务的方式将数据行从一个数据块转移到另一个数据块。收缩过程中,表仍然可以进行DML操作
当然,事务要能够进行DML操作,还是需要等待收缩引起的事务锁释放。
收缩虽然是事务,但是数据并没有发生变化,因此不会引起触发器的触发。
使用shrink的前提条件
1、表所在的表空间必须使用ASSM(自动段空间管理)
2、在收缩表上必须启用row movement选项
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937388rcGX.png"></a>
两个条件都具备了。可以对表进行收缩。
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_13359373892E7u.png"></a>
为test3表添加一些数据并删除,以演示收缩
收缩操作分为两个阶段
1、压缩
2、降低HWM
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937389ytc7.png"></a>
可以分开两个阶段进行执行,业务运行期间执行第一个阶段,业务不繁忙或者停止的时候,进行第二个阶段。
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937390WUvz.png"></a>
如果表上有相关的对象,例如索引,该如何收缩?
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937390EsEq.png"></a>
我们不太清楚哪些表需要进行收缩,该如何?使用segment advisor
可以对整个的表空间,也可以对具体的对象进行分析。
截断表
删除记录可以采用delete,delete是DML事务,对表的记录加锁,产生重做日志,消耗undo空间,消耗资源较多,执行时间较长
对于大表数据,我们建议采用truncate
Truncate是一个DDL语句,只更新数据字典
将数据字典里面该表所占用空间记录全部删除
将表所占用的数据块全部释放
将HWM下降到最低
因为是DDL,执行完毕就不能回滚
问题
如果需要截断的表达到几十个GB,那么释放数据块的工作可能会花费很长的时间(可能超过10个小时),在这个过程中,被截断的表是不能被使用的。
截断并不意味着drop,因此我们还是需要使用这些表
如何解决,10g中给出了解决方案
1、更新完成数据字典以后,不立即释放全部数据块(这个操作花费时间多)
2、数据块没有释放,仍然被表所拥有,但是表上的HWM已经下降到最低了
3、系统空闲的时候,分多次释放数据块,每次释放部分空间
这样truncate就能将对系统的影响降到最低
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937390rqyM.png"></a>
只是更新了数据字典,降低了HWM,数据块并没有实际的释放。
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_13359373916dq4.png"></a>
查看所占用的块
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937391i5P4.png"></a>
数据库可以正常的使用。
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_13359373922rzo.png"></a>
将表的空间释放到1m,可以看到收缩了一些块
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_13359373929DtT.png"></a>
虽然将表的空间释放到0m,但是表中已经添加了数据,因此没有完全释放。通过上面的方式,我们可以阶段性的释放数据块。
删除表的操作
1、删除表的操作属于DDL命令,也就是只是更新数据字典信息,数据字典信息存在system表空间中,因此即使表属于只读表空间中,也能删除表
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937392FyFD.png"></a>
2、如果这个表是其他表的父表,或者说其他表上有外键引用了这个表,删除这个表的时候,需要加上参数cascade constraint,删除主表以后,引用这个表的外键也被删除。(外表并没有被删除,删除的只是外键)
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937393fmfY.png"></a>
创建一个表testdrop
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937393Y5X8.png"></a>
对一个列名进行修改
数据一致性问题
为了更好的提供数据的一致性,Oracle提供了约束
Oracle提供了5种约束
1、非空not null,在列上定义,该列必须有值
2、唯一unique,在列上定义,不同的数据行上,该列的值不能重复,唯一约束是借助索引建立的,建立唯一约束的时候,如果该列上没有索引,那么就建立一个唯一索引
3、主键primary key,在列上定义,和唯一键唯一的不同之处就是不能为空,unique列上可以有多个null,主键也是借助索引建立的
4、外键foreign key,发生在两个表之间,外键在子表上定义,但是引用(参考)了父表的列,被引用的父表上的列必须有主键或者唯一键
5、检查check,在表上定义,类似于where条件,表里所有的记录必须满足指定的条件
建议:对于数据一致性问题,约束能够实现的,尽量在数据库里面实现,不要放在应用里面实现,主要是为了集中实现。
约束的状态
enable和disable状态:创建约束以后,对表的数据进行插入或者修改时,是否进行校验
validate和novalidate状态:创建约束时,是否对表里现存的数据进行校验
我们看一下组合情况
1、enable+validate:既要校验已经存在的数据,也要校验新进入的数据
2、enable+novalidate:不校验已经存在的数据,只校验新进入的数据
3、disable+validate:一种矛盾的状态,有这种约束的表不能进行DML
4、disable+novalidate:相当于没有创建约束
1和 2使用的最多,创建约束以后,对新进入的数据就要使用约束,对于已经存在的数据,有的情况下进行校验,有的情况下,不进行校验
在对表进行创建或者编辑的时候,可以创建约束。
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937394qW7W.png"></a>
1、禁用:是否对新加入的数据启用约束
2、验证:是否对已有数据进行校验
3、可延迟:后面会详细的讲,这个涉及到一个约束启用时机的问题
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937394LM3I.png"></a>
建立了一个表,这个表上建立了一个主键约束。
我们可以对这个主键约束进行enable、validate、disable、novalidate的选择
我们还可以修改这个约束的名字,这个特性是从9i R2开始
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_13359373945ZfV.png"></a>
约束校验数据的时机
默认情况下,一旦发生DML操作,Oracle会立即判断变化后的数据是否违反了约束,如果违反,立即回滚该DML所进行的修改
我们还可以选择:约束在事务提交的时候进行校验,这叫做延迟约束(deferred constraint)
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937395O4IT.png"></a>
创建表sales,查看一个约束的触发。
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937395hcZW.png"></a>
注意:回滚只是回滚引起约束冲突的这条语句的修改。对于已经实行的操作,不会进行回滚。
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937396DTAu.png"></a>
表示可延迟,但是默认是initially immediate,发生DML操作立即校验数据。
事务提交时进行校验
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937396GO9B.png"></a>
插入一条数据,只有在提交的时候才会触发延时约束
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937396Cb5s.png"></a>
如果只是定义了这个约束的deferrable属性,表示可延迟,默认是immediate,马上进行校验。有什么意义呢?
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937397OYva.png"></a>
这个会话涉及的所有的表,只要是定义了deferrable属性的约束,全部都延迟校验。
分区表
上千万条记录的表,称之为大表,在管理和性能上会有问题,如何解决?
1、一个非常大的表分布在不同的磁盘上,如果其中一块磁盘坏了,就会导致整个表的数据丢失
2、对大表进行数据迁移,即使只是对部分数据进行迁移,也必须以整个表为单位进行操作
3、从上千万条的表中检索数据,花费的时间会很多
Oracle引入了分区表的概念
1、分区表是多个小表的组合,每一个小表叫做一个分区
2、数据存放在分区里面,每个分区对应一个物理的segment
3、不同的分区可以具有不同的存储属性,可以存放在不同的表空间中
4、分区表的总表是一种虚表,没有对应的segment,分区表具有多个segment
5、逻辑上是一个表、相同的列、相同的约束
改进
1、不同的分区放在不同的磁盘上,既是一个磁盘损坏,只有这个损坏磁盘上的数据不能访问,其他数据还是能够访问
2、可以以分区作为单位进行备份、迁移时直接将分区里面的数据与其他表进行迁移
3、性能上,如果以列c1建立的分区,那么当where条件中使用c1的时候,数据库自动判断,直接到该分区里面检索数据,不需要对整个表进行扫描
1、范围分区
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937397GK7h.png"></a>
maxvalue这个有必要定义,否则当插入的数据大于2012-12-21的时候,就会报错(世界末日^_^)。
2、Hash分区
对列的值进行hash函数的运算以后,分布到分区里面,这样做的最大的好处就是能够实现分区数据的平均分布,我们在定义分区的时候,分区的个数应该是2的幂,例如2,4,8,16,否则出现数据分布不平衡,失去了hash分区的意义
当列中的数值不容易划分范围的时候,我们可以使用hash分区
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_13359373987hZh.png"></a>
3、列表分区
分区列的取值是一些指定的离散值的时候,该数据行就进入某个指定的分区
4、范围hash组合分区
1、首先根据范围进行划分,然后将每个范围划分的数据进行hash分区,分布到不同的hash子分区上,范围分区以后,并没有产生segment,对范围进行hash的时候,才会产生实实在在的segment
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_13359373988GZc.png"></a>
1、根据范围对表进行了范围划分,分成4个区。
2、对每个区的hash划分默认是根据id,分成4个分区
3、pmax分区没有使用默认值,而是分成了2个分区
因此一共产生了14个分区。
5、范围列表组合分区
首先对列进行范围划分,然后对每个分区进行列表分区
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_13359373989Twb.png"></a>
一共是6个分区
p1_1、 p1_2在ts01上
p1_3在ts02上
p2_1、 p2_2在ts03上
p2_3在ts04上
索引组织表
一般的表都是以堆来组织的,这是一种无序的组织方式。Oracle还提供了另外一种有序的表,索引组织表,简称IOT
IOT表上必须有主键,表里所有的数据都存放在主键所在的索引的叶子节点里
在索引叶子节点里的索引条目里,不仅存放了被索引的列的值,还存放了其他列的值
对于总是通过主键访问数据的表来说,比较适合使用IOT表
普通表通过主键索引访问数据表,至少读取两个数据块
1、索引块
2、表的数据块
IOT表
只要读取索引块就可以
IOT是一个虚表,实际存放在索引segment里
在IOT的基础上建立索引,叫做二级索引,使用二级索引查找数据,存在两个阶段
1、在二级索引里面存储的是逻辑ROWID,就是主键值
2、二级索引进行查找的时候,首先是根据二级索引查找到逻辑ROWID,因为逻辑ROWID就是主键值,因此还需要进行一次二次查找,根据主键值再进行一次索引查找。
也就是二级索引最终还是要转化成对主键列的查找
二级索引里面存储的是逻辑ROWID、不是物理ROWID
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937399LyDJ.png"></a>
1、必须创建一个主键约束
2、指定索引组织表的方式
如果pctthreshold限制的列是c1开始后面的列,但是including限制的列是c2开始后面的列,那么以pctthreashold为准
如果我们在访问IOT表的时候,经常访问id和c1两个列,如果把c2、c3、c4也放在索引的叶子节点里面,那么会造成索引叶子节点的增多,单个叶子节点里面的行数减少,这样访问索引的速度就会降低。因此我们希望后面的三个列不要放在索引的叶子节点里面,而是放在溢出segment里面
通过两个参数来指定
1、留在索引块里面的数据空间占总数据块大小的百分比,0-50%,id列+c1列总共占的字节数/索引块的大小,超出这个范围的列不放在索引块里面
2、表示从c2开始,后面所有的列都放在overflow segment里面,不放在索引块里面
簇表(cluster table)
两种类型的簇表:索引簇表、hash簇表
索引簇表的经典用法
1、主名细表关连查询,主表和明细表经常进行关连查询,适合使用这种表结构
2、建立一个 cluster segment,segment里面有extent,extent里面有block
3、簇表的数据就是存放在cluster segment里面
4、因为表并没有segment,因此是一个虚表
5、在cluster segment上面建立索引(主表和明细表的关联键)
6、将表关联到cluser segment上,根据关联键,将主表和明细表的记录放在同一个数据块里面。
7、因为总是关连查询,因此只要扫描一个数据块就可以取到两个表的数据
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937399qcEi.png"></a>
建立一个聚集段
在关联键上建立索引
将表建立到聚集索引上
Hash 簇表
1、一个hash簇只能关联一个表
表有一个簇列,表在插入segment的时候,对簇列进行hash运算,得到一个该记录所在的数据块的位置
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_13359374002s3T.png"></a>
表示phone_no的值最多不超过10000个
对于每一个hashkeys值所对应的所有记录行来说,在segment头部都会有一块空间,来存放这些数据行的地址,size表示每个hashkeys 所能使用的空间大小,上面是50个字节。
临时表
1、临时表所存放的数据都是临时使用的,这些数据用完以后就可以删除
2、临时表的数据被session所专用
同一个临时表,多个客户端使用,客户端将数据暂存在临时表里面
客户端只对自己的数据进行处理,互相之间并不影响
因为临时表内的数据不共享,被session所占用,因此没有DML锁
Oracle能够自动清除临时表里的数据
1、退出session的时候,也就是用户中断数据库连接的时候,自动清除与该session相关的,位于临时表里的数据,通过on commit preserve rows指定
2、用户提交或者rollback时,自动清除与该session相关的,位于临时表里的数据,通过on commit delete rows指定
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_13359374009J1d.png"></a>
创建两个临时表,分别使用两种不同的模式
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937400qoWZ.png"></a>
上面是在session A里面
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937401HBgq.png"></a>
另起一个session,看不见另一个session的数据。
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937402GKNU.png"></a>
临时表里的数据都是存放在临时表空间里面
当前正在使用临时表空间的session信息
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_13359374029K4G.png"></a>
Session A里面
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937403LpvK.png"></a>
Session B里面
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937403HzCl.png"></a>
临时表所占的区与块的情况
索引
索引的主要目的就是提高查询速度,分为B树索引和位图索引
B树索引(平衡树索引)
1、索引也属于一种segment,里面存放了用户数据,与表一样需要占用空间
2、索引里面的数据存放形式和表不一样,是有序排列的
3、索引占用的空间比表小得多,主要作用是为了加快对数据的搜索速度,也用来保证数据的唯一性
4、索引对DML操作会产生额外的负担
索引分为很多种
分区索引、非分区索引、常规B树索引、位图索引、反转索引
B树索引是最常见的索引
B树索引是一种典型的树结构,而且是一颗平衡树
1、叶子节点(leaf node):包含的条目中具有指针,指向表里的数据行,叶子节点之间互相指向,只要表里的记录行中,被索引的列的值不为空,就会在索引叶子节点里存在一个索引条目,如果被索引的列的值为空,则不会在叶子节点中存放对应的条目
(属于表的数据块里的数据叫做数据行,属于索引的数据块里的数据叫做索引条目)
2、分支节点(branch node):包含的条目指向索引里其他的分支节点或者叶子节点
3、根节点(root node):一个B树索引值有一个根节点,位于树的顶端的分支节点
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937404ef5b.png"></a>
一个索引条目由两个字段组成
第一个字段表示当前该分支节点块下所链接的索引块中所包含的最小值
第二个字段4个字节,表示所连接的索引块的地址,该地址指向下面所链接的索引块
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937404lr3m.png"></a>
根索引块中包含三个索引条目,分别指向三个下级索引块
注(分支索引块中的一个索引条目指向一个下级索引块,叶子索引块中的一个索引条目指向一条表记录)
1、0表示这个索引条目指向的下级索引块的最小值是0
2、B1指向这个索引条目所指向的下级索引块的地址
3、L1中的0就是一个键值,R1是0这个键值指向的数据行的物理ROWID
如何估算每个索引能够包含多少条目,以及对于表来说,能产生索引的大小
我们最好在收集表的统计信息的时候,同时收集索引的统计信息。看一下索引一共占用了多少数据块。
B数索引的管理机制
管理分为两种情况
1、在一个充满了数据的表上创建索引时,索引是怎么管理的?
Oracle会扫描表里的数据,并对其进行排序,然后生成叶子节点,生成所有的叶子节点以后,根据叶子节点的数量生成若干层级的分支节点,最后生成根节点
2、创建索引以后,一行接着一行向表里插入或者更新或者删除数据,索引是怎么管理的?
1、当一开始在一个空的表上创建索引的时候,该索引没有根节点,只有一个叶子节点
2、随着数据不断的插入表里,该叶子节点的索引条目也在不断的增加
3、当该叶子节点充满索引条目而不能再放下新的索引条目时,该索引就必须扩张,也就是在获取一个可用的叶子节点,这个过程叫做索引的拆分(split)
4、索引包含了两个叶子节点,两个叶子节点需要一个上级的分支节点,于是索引就有了三个索引块(一个根和两个叶子)
叶拆分的两种情况
叶子节点的拆分分为两种情况
1、一种是插入的键值不是要插入的索引块里包含的最大值
2、另一种是插入的键值是要插入的索引块里包含的最大值
第一种情况:当一个非最大值要进入索引,但是发现所应进入的索引块不足以容纳当前键值,Oracle会按照以下步骤进行叶子节点的拆分
1、获得一个新的可用的索引数据块
2、将当前充满了的索引块中的索引条目分成两部分,一部分是具有较小的键值、另外一部分是具有较大的键值,Oracle将具有较大键值的部分移入新的索引数据块,而较小键值的部分保持不动
3、将当前键值插入合适的索引块中,可能是原来空间不足的索引块,也可能是新的索引块
4、更新原来空间不足的索引块的指针信息,使其指向新的索引块(分支索引块之间是双向链表)
5、更新位于原来空间不足的索引块右边的索引块里的指针信息,使其指向新的索引块
6、向原来空间不足的索引块的上一级分支索引中添加一个索引条目,该索引条目中保存新的索引块里的最小值,以及新索引块的地址
对于第二种情况,也就是插入键值是要插入索引块里包含的最大值时
分裂过程相对简化,不再进行上面第二步中索引块的分裂,直接将新的索引条目插入新的索引块中
当叶子节点越来越多,导致原来的根节点不足以存放新的索引条目的时候,则该节点就必须进行分裂,根节点的分裂流程如下
1、从索引段的可用列表上获得两个新的索引数据块
2、将根节点的索引条目分成两个部分,这两部分分别存放在两个新的索引块中,从而形成了两个新的分支节点
3、更新原来的根节点的索引条目,使其分别指向这两个新的索引块中
这时索引层次变成了两层,根节点索引块在物理上始终都是同一个索引块,而随着数据量的不断增长,导致分支节点又要进行分裂,分支节点的分裂过程与根节点类似步骤如下
分支节点的分裂
1、从索引可用列表上多的一个新的索引数据块
2、将当前用满了的分支节点里的索引条目分成两个部分,较小键值的部分不动,而较大键值的部分移入新的索引块
3、将新的索引条目插入合适的分支索引块中
4、再上一层分支索引中添加一个新的条目,使其指向新加的分支索引
分支索引和根节点会随着数据量的增加继续拆分,步骤同上
根节点分裂以后,索引的层次增加,根据索引的分裂机制来看,一个B树索引始终都是平衡的,平衡指的是每个叶子节点与根节点的距离是相等的
当插入的键值始终都是增大的时候,索引总是向右扩展
当插入的键值始终都是减小的时候,索引总是向左扩展
删除表的操作,对索引的影响如下
1、当删除表里的一条记录时,其对应于索引块里的条目并不会被物理的删除,只是做了一个删除标记
2、当一个新的索引条目进入一个索引叶子节点的时候,Oracle会检查该叶子节点里是否存在被标记为删除的索引条目,如果存在,则会将所有具有删除标记的索引条目从该叶子节点里物理的删除,避免叶拆分
3、当一个新的索引条目进入索引时,Oracle会将当前所有被清空的叶子节点收回(该叶子节点中所有的索引条目都被标记为删除),从而再次成为可用索引块
尽管被删除的索引条目所占用的空间大部分情况下都能够重新使用,但是仍然存在一些情况可能导致索引空间被浪费,并造成索引数据块很多,但是索引条目很少的情况,这时该索引可以被认为出现碎片
导致索引出现碎片的情况主要包括以下几种
1、不合理的较高的PCTFREE
2、索引键值持续增加(例如使用sequence生成序列号的键值),同时对索引键值按照顺序连续删除,这时可能导致索引碎片的发生
原因:某个索引块中删除了部分的索引条目,只有当前键值进入该索引块的时候才能将空间收回,而持续增加的索引值永远只会插入排在前面的(右面)的索引块中,索引空间几乎不能回收
3、经常被删除或者更新的键值,以后几乎不再会被插入,情况同上
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_133593740559DP.png"></a>
如果这个值小于50%,那么就认为存在明显的碎片。
更新对索引条目的影响,可以认为是delete和insert的组合。
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_13359374058jGK.png"></a>
补充一点:
双向链表的作用
如果一个以范围作为查询条件的查询,例如20&lt;c1&lt;30,那么首先找到键值20,然后沿着20一直向上找,一直找到30为止,整个的路线是沿着双向列表,而不需要重复的回到根节点。
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937406VsCg.png"></a>
位图索引:组织形式和B树索引相同,也是一颗平衡树
B树索引在叶子节点里为每一个键值维护一个索引条目
位图索引的特点
应用场合:作为索引的列的值只有少数的几个
如上图
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937406KOXx.png"></a>
1、表T里所有的记录在列c1上只具有三个值:01、02、03
2、在c1上创建了位图索引
3、叶子节点中,只有三个索引条目,每个c1列的值对应一个索引条目
4、位图索引条目中还包含表里第一条记录所对应的ROWID以及最后一条记录所对应的ROWID
5、索引条目的最后一部分则是由多个bit位组成的bitmap,每个bitmap对应一条记录,这个表一共19条记录
6、因为列的值就三种,因此三个位图加起来就是19个1
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937407kdjl.png"></a>
当发出where c1=’01’这样的sql 语句的时候,Oracle会搜索01所在的索引条目
然后搜索该索引条目中的bitmap里所有bit位,第一个bit位为1,则返回第一条记录所在的ROWID(根据索引条目所对应的 start ROWID加上行号得到该记录所在的ROWID),第二个bit为0,则说明第二条记录上的c1值不为0,如果索引为空,也会在位图索引里记录,相应值为0
如果索引列上不同值的个数比较少的时候,例如性别,使用位图索引效果很好
如果在主键上建立位图索引,效果不如B树
如果位图索引经常被更新的话,不适合位图索引,因为更新一个条目以后,需要在两个位图索引条目上同时更新,会锁定两个索引条目,降低了并发性
位图索引比较适合数据仓库,不适合OLTP
管理索引
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937407DWrW.png"></a>
建立索引
重建索引
重建索引有两种方式
1、drop + create
2、alter index ….rebuild
第二种方式的好处
1、使用原索引的叶子节点作为新索引的数据来源
索引数据块相对表数据块来说要少很多,因此减少了IO
原索引叶子节点已经排好序,因此节省了排序的工作
2、8.1.6开始,rebuild的时候,可以加上online,索引重建过程中,用户可以继续对原来的索引进行修改,也就是说可以继续对表进行DML操作
两种方式的相同之处
1、他们都可以通过添加PARALLEL提示进行并行处理
2、都以通过添加NOLOGGING,使的索引的重建生成最少的日志条目
3、8.1.5开始,就可以通过添加compute statistics选项,在重建索引的过程中就生成Oracle优化器所需要的统计信息
重建索引的好处:减少了索引所占用的空间,索引块减少以后的好处如下
1、索引扫描的物理块数减少
2、内存占用减少
索引会加重DML的负担
1、每添加一个索引,DML语句的响应时间增加三倍
2、Oracle建议每个表上的索引不要超过7个
3、查询如果不怎么使用索引,那应该删除这个索引
Oracle提供了一种方法,用来监控某个索引是否被使用过
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937408bqAi.png"></a>
在监控的这段时间内,索引没有被使用过。
视图
可以把视图理解为给一条SQL语句起了个名字,这个名字就是视图名字,使用视图的好处如下:
1、可以屏蔽SQL语句的复杂性,使用一个视图的名字就可以代表一个复杂的SQL
2、可以管理权限,不同的用户创建不同的视图,访问的列不同,达到控制访问的目的
<a href="http://bearlovecat.blog.51cto.com/attachment/201205/2/1293914_1335937408M3QC.png"></a>
本文转自bear_cat51CTO博客,原文链接:http://blog.51cto.com/bearlovecat/850056 ,如需转载请自行联系原作者