undo 表空间管理
1、对于dml语句来说,只要修改了数据块,oracle数据库就会将修改前的数据块保留下来,保存在undo segment里面,而undo segment则保存在undo表空间中
2、undo的管理
自动undo管理(oracle9i开始)aum
手工undo管理mum
9i以后,就建议使用aum,因此就不再讨论mum
一条dml语句的执行流程update t set coll=‘a’ where coll=‘b’
1、在shared pool里面进行解析,从而生成执行计划
2、根据执行计划,得出coll=‘b’的记录存放在10号数据文件的54号数据块里面
3、服务器进程首先在buffer cache寻找一个可用的undo数据块(如果一个事物已经提交,那么这个事务曾经使用过的undo数据块就可以被使用),如果没有发现,则到undo表空间里找到一个可用的undo数据块,并调入到buffer cache。假设获得的undo数据块号为24号,位于11号undo数据文件里
4、将改变前的值,也就是b放入24号undo数据块(buffer cache中)
5、由于undo数据块发生了变化(只要是数据块发生变化,那么就产生重做记录),于是产生重做记录,假设重做记录号是120
6、在buffer cache里面找到54号数据块,如果没有,则从10号数据文件调入
7、将改变后的值,也就是a放入54号数据块
8、由于数据块发生了变化,于是产生重做记录,假设重做记录号是121
9、控制权返回给用户,如果使用sqlplus,那么表现为光标返回
10、用户发出commit命令,触发lgwr,将120、121这两个重做记录写入联机重做日志文件中,将54号、24号两个数据块头部所记录的事务状态标记设置为已提交,控制权返回给用户,如果使用sqlplus,那么表现为光标返回
11、这个时侯,54号和24号数据块并不一定被dbwr写入数据文件,只有在脏数据块的数量达到一定程度的时候才会被写入
事务提交以后,该事务所使用的undo数据块就可以被覆盖,上面的例子中,第10步用户提交以后,24号undo数据块就可以被覆盖
undo的作用
1、提供读一执性
2、回滚事务
3、实例恢复
读一致性
一个场景描述
读一致性是相对脏读而言的,表t中有10000条记录,获取所有的记录需要15分钟的时间,当前时间为9点整,香港服务器,用户发出一条select * from t命令,该语句在9:15完成。当用户执行该语句到9:10分的时候,另外一个用户发出了一条删除命令,将最后一条记录删除,并且进行了提交。
到9点15分的时候,用户返回了多少条记录。
如果是9999条,那么就是脏读、如果是10000条,那么就是读一致性。
oracle不会出现脏读,提供读一致性,而且没有阻塞dml操作
oracle如何实现读一致性呢?
1、用户在9点发出select语句的时候,服务器进程会记录9点那个时刻的scn号(scn号是以时间(timestamp)作为参数的一个函数返回值,调用函数(默认以timestamp为参数)随时可以返回这个时刻的scn号,可以使用函数在scn和timestamp之间进行转换),假设该scn号是scn9:00,那么scn9:00一定大于等于记录在所有数据块头部的itl槽中的scn号(如果有多个itl槽,scn最大的那个)
2、服务器进程扫描t表的时候,会把扫描的数据块头部的itl槽中的scn号与scn9:00进行比较,哪个更大。如果数据块头部的scn小于scn9:00,那么说明这个数据块在9:00以后没有更改过,可以直接读取,如果数据块头部的scn号大于scn9:00,则说明该数据块在9:00以后更改过,已经不是9:00那个时刻的数据了于是要借助undo块
3、9点10分,用户更改了t表的最后一条记录并提交(无论是否提交,只要是更改了t表,用户就会去读undo数据块),假设被更改的是n号数据块,那么n号数据块头部的itl槽中记录的scn被修改为scn9:10,当服务器进程扫描到这个数据块的时候,发现itl槽中的scn9:10大于scn9:00,说明该数据块在9:00以后被更新了,于是服务器进程到n号块的头部,找到scn9:10所在itl槽,由于itl槽记录了对应的undo块的地址,于是服务器进程找到undo数据块,结合undo数据块给用户提供读一致性。
进一步复杂化问题
1、9点10分,更新了数据并且进行了提交,9点11分又对该数据块进行了更新并且提交(假设数据块只有一个itl槽)
2、那么该itl槽记录的就是scn9:11
3、这种情况如何处理,秘密在于undo数据块中,除了记录改变前的数据以外,因为数据块的itl槽也发生了变化,因此也进行了记录,而itl槽中记录了undo块的地址。9:00的时候数据块的itl槽中记录了数据块的scn是8:50和对应的undo块,9:10分的时候数据库的itl槽中记录了数据块的scn是9:10和对应的undo(undo1),9:11分的时候数据库的itl槽中记录了数据块的scn是9:11和对应的undo(undo2),undo2中记录了9:10的数据,以及9:10数据的undo地址undo1,undo1中记录了9点以前的数据以及9点以前的undo数据
当用户进行查询的时候,服务器进程扫描到n号数据块,发现scn9:11,大于scn9:00,从itl槽中找到undo块的地址(undo2),undo2中记录了改变前的数据和改变前的数据库的itl槽,发现undo2对应的scn是9:10还是大于9:00,根据undo里面记录的itl槽的改变前信息,继续向前找,找到undo1,发现scn是8:50,小于9:00,使用这个undo的数据
如果在向前寻找的时候,没有找到scn小于9:00的数据(因为时间比较长,而且事务已经提交,所以回滚段可能被覆盖),数据库就会出现一个经典的错误ora-01555(snapshoot too old),但是不会出现脏读的情况。
最核心的就是:数据块的数据发生改变,itl槽也发生改变。这两条信息都会存储到undo中。因此如果一个undo足够的大,那么一个数据块的所有的undo数据块是可以串联起来的。可以从最近一直找到非常远的过去。从数据块开始往前找,一直找到很久以前的scn。这个数据块所有的变化都能够找到。
itl槽中记录着这个数据块的undo数据块的地址。
一个数据块中存储很多条数据,update、insert、delete等dml操作,都会影响数据块的scn号。数据块的scn号反映了数据块的变化过程。
回滚事务:错误或者rollback命令都会产生回滚
根据itl槽中记录的undo数据块的地址,找到undo数据块,恢复数据。
实例恢复
回滚段的头部记录了事务表,每一个事务是否提交等信息都存储在里面。
根据事务表的信息进行实例恢复。
配置aum
oracle9i开始,我们不再使用手工的管理方式,因此“manual”不再使用,使用“auto”
使用aum需要配置两个参数
指定这两个参数以后,剩余的工作让oracle来处理,例如undo segment的创建、扩展、收缩、删除等。
如果指定了undo_management,但是在指定undo_tablespace的时候,指定了一个错误的回滚表空间,实例启动的时候,会报错。
如果没有指定回滚段表空间,系统查找第一个可用的回滚段表空间,如果没有找到,那么就使用system表空间中的回滚段,这不是我们希望看到的,因为对性能影响很大。
因此两个参数都要很好的规划
事务、undo segment
发生dml操作的时候,服务器进程会选择一个undo segment,具体算法如下:
1、首先尝试将每一个undo segment绑定一个事务,也就是每个undo segment上只有一个事务使用
2、如果不能发现完全空闲的undo segment,所有的undo segment都与事务绑定
3、系统尝试将脱机的undo segment联机
4、如果没有可用的undo segment进行联机,则会尝试创建一个新的undo segment
5、如果上面的步骤都没有成功(例如没有可用空间了,不能创建undo segment),算法会尝试寻找最早使用的undo segment,这种情况下,不同的多个事务会在同一个相同的undo segment里同时进行
6、每隔12个小时会收缩一次,删除那些idle状态的extent
7、dml操作需要undo时,发现空间不够,则会唤醒smon进行一次收缩,将undo segment里面暂时没有使用的extent拿过来使用
oracle 在提供一致性读的过程中,具体的步骤如下
1、确认读时刻的scn
2、搜寻所有的数据块的scn要求小于读时刻的scn
3、如果搜寻的scn小于读时刻的scn,直接读取
4、如果搜寻的scn大于读时刻的scn,根据数据块里面的itl槽里面记录的undo信息,找到改变前的数据、如果scn还是大,顺着itl槽信息串联起来的undo块继续向前找
5、如果没有找到小于读时刻的scn的数据块,那么就报错ora-01555
事务提交以后,undo回滚段就可以被覆盖,而且我们在寻找undo数据块的时候,这个被寻找的undo数据块很可能已经被提交,因此出现ora-01555错误不可避免。oracle是如何来解决这个问题的呢?
oracle定义了一个参数undo_retention
这个参数以秒为单位,表示当事务提交或者回滚以后,该事务所使用的undo 块里的数据需要保留多长时间。
当保留的时间超过undo_retention所指定的时间以后,该undo块才能够被其他事务覆盖。
当我们使用aum的时候,并且设置了undo_retention以后,undo块的状态就存在4种
active:表示正在使用该数据块的事务还没有提交或者回滚
inactive:该数据块上没有活动的事务,该状态的undo可以被其他事务覆盖
expired:该数据块持续inactive的时间超过undo_retention所指定的时间
freed:该数据块是空的,从来没有被使用过
在aum模式中,事务可以在不同的undo segment之间动态交换undo空间,也就是在不同的undo segment里交换extents。
我们来看一下,一个事务需要更多的undo空间的时候,是如何进行处理的?
1、获取undo表空间里可用的、空的extents(segment的最小分配单元是extent)
2、获取其他undo segment里的expired状态的extents
3、如果undo表空间里的数据文件启用了自动扩展,则数据文件进行自动扩展
4、如果undo表空间里的数据文件没有启用自动扩展,则获取undo segment里的inactive状态的extents
5、如果还是没有获得可用空间,报空间不足的错误
1、undo表空间中一共存在5个undo segment
2、每个undo segment包括7个extents