29、undo_2_1(事务槽、延迟块清除、构造CR块、ora-01555)

事务槽(不同于事务表里面的槽位(这个事务槽在数据块的头部))


图解:
一个事务开始,要做的事情:
第一,事务表里面找槽位(undo段的段头块里有事务表,事务表有槽位,每一个槽位记录一个事务);
事务表里的槽位里记录的信息有:
1、xid(事务ID):(undo段的段号,段里面的第几个槽(槽号),覆盖数);
2、cflag(commit flag):提交标志;
3、SCN号(提交SCN号);
4、dba(数据块地址):指向事务使用的最后一个块;

第二,做DML操作,要修改某个数据块里面的一个数据行,在修改数据行以前,oracle会做一件事情:对于普通数据块来讲,在数据块的头部默认有两个事务槽,最多可以有255个。
事务槽里面含有的信息:
1、xid;
2、uba(undo block adress):undo块地址;
3、提交标志;
4、SCN号;

事务开始了,要修改数据块,必须先获取到数据块里面的事务槽;假设0号事务槽里面没有未提交事务(就是没有active事务槽的时候),这个事务槽就可以被覆盖;要修改多行数据的时候,用一个事务槽就可以了;然后将事务信息写入事务槽;

现在要修改数据行了,会在数据行的头部写入一个数字(比如:0(事务槽的槽号)),表示在数据行上加了一个锁(也就是说这个数据行正在被0号事务槽里面所对应的事务修改),然后指向0号事务槽,然后事务槽里面的xid指向这个事务的事务源;同一个事务可以修改数据块里面的多行数据(这里修改了3行数据),都指向了同一个事务;另外还修改了另外一个数据块,然后也指向同一个事务源(一个事务可以同时修改多个数据块);

关于事务的提交标志,在很多地方有,比如:在每一个数据块的事务槽里面有提交标志,在事务表里面的槽位里面有提交标志,并且都有SCN号;

现在描述一个场景:
假设,一个事务修改了100W个块,然后要commit提交;要提交的时候oracle会做几件事情:
1、清锁:需要把100W个块里面的每个数据行前面的锁标记清除了;
2、清理数据块的事务槽:就是把事务槽里面的信息清除了;
这两个清理的会很慢(非常消耗资源),因为涉及到100W个块;
3、清理undo段里面的事务表;

这个清理的比较快(不消耗资源);
用户提交的时候,希望马上提交成功,但是提交的时候第一和第二件事情很慢,很消耗资源,所以oracle在提交的时候,只清除事务表里面的信息(这个是必须要做的事情),记下提交时候的SCN号,然后第一和第二件事情oracle也会尽量的去做一些(比如,现在是100W个块,然后呢,只修改1000个块),意思意思;
事务表里面的清了,数据块里面的事务槽还没有清理完,然后oracle下次再访问块的时候,假设读到之前修改的一行数据时,发现上面有锁标记,表示上面有活动的事务,然后找事务槽里面的事务,显示事务没有提交,这时候再到事务表里面找对应的事务,但是已经显示提交了,oracle就知道当时提交的时候,数据块里的事务槽没清理,所以oracle下次再访问块的时候就顺便清了,把锁标记清了,事务槽里面的信息清了,清理完之后就接着访问

快速提交

所以,只要事务表里面的信息清了,数据块里面的信息有没有清没关系;oracle里,在提交的时候,把事务表里面的信息清了,其他的地方意思意思的清理一下,这种提交机制叫做:快速提交

延迟块清除

然后呢,再读到那个块的时候,顺便清了,然后修改数据块,修改数据块就要修改锁标记,也要修改事务槽里面的提交标记(修改为未提交),还有修改SCN号(改为null),修改的时候就会产生redo,这种情况叫做:延迟块清除

有这么一种情况:就是事务提交的时候,提交了以后,因为数据库的压力比较大,只清除了事务表里面的信息,其他的都没有清除;假设这时候,事务表里面的槽位是:5号段,2号槽位,第100次被覆盖,因为这个事务提交了,这时候就被覆盖了,就变成了:5号段,2号槽位,第101次被覆盖;然后数据块里面的事务槽因为没有清除,记录的还是:5号段,2号槽位,第100次被覆盖,这时候就去找,但是在undo事务表里面已经是:5号段,2号槽位,第101次被覆盖了,oracle就认为已经提交了,这时候接着就清除锁和事务槽,更改SCN号,更改提交状态,

快速提交和延迟块清除,都是为了提高提交的速度

构造CR块(consistent read)(读一致性)


图解:
一个undo段,假设里面有三个事务(T1、T2、T3),然后修改数据块里面的一行数据,修改数据之前,要把修改之前的数据放到undo里面事务T1对应的undo块里,修改前的值是1,修改后的值是2;

然后,select要访问这个数据块的这个数据行,oracle访问的时候,发现这个块上有事务槽,显示事务没有提交,然后oracle就根据xid找undue段里面的事务表对应的事务,看看这个事务是否已经提交,然后发现这个事务确实没有提交;

假设现在对于sp1来说,要读这个数据块,但是在这个数据块上sp2在修改并且还没有提交,这时候sp1是不能读未提交事务的,也就是说,对于任何的会话,任何的server process都是不能读未提交事务的;

这时候oracle会在buffer cache里面再找一个空块,把sp1正在修改的数据块里面所有行的数据取出来放到这个空块里,因为里面有一行有未提交事务,它就找事务槽的uba地址,然后找到undo数据,把修改前的数据取出来,还原事务修改前的值,再放到空块里面去,这就构造了一个块出来,这个块叫做:构造CR块

所以在oracle数据库里面,DML不阻塞select,也就是:修改不阻塞读;就是当oracle访问数据行的时候,去事务槽和undo事务表确认事务是否提交,未提交会在buffer中找到一个空块,找到undo中的源数据,构建一个CR,所以修改数据行的时候不会堵塞读。只读已提交事务,不读未提交事务。

崩溃恢复


图解:
数据库正在正常的运行着,