1、一致性读和事务

一个事务开始以后,分配undo段头事务表和undo块,事务表指向undo块,数据块中有事务槽,底下有数据行,数据块事务槽中事务ID指向事务表,事务表指向undo块,数据块事务槽也指向undo块,修改数据块中的数据行,修改的时候有指向关系,数据行指向数据行对应事务槽,A事务修改数据行以后,这个数据行指向数据块的A事务的事务槽,事务槽中有事务信息也有回滚信息,回滚块信息指向undo块,事务信息指向undo段头的事务表,B绘画访问同一个数据行,发现这个数据行有锁定标记,就是数据行有一个数据指向事务槽,在行发现有链指向事务槽说明这个事务没有提交,这时不能读,B就构造一个cr块,这个cr块里面没有修改的数据行写上,修改的数据行根据锁定标记找到事务槽,根据事务槽中uba信息找到undo块,undo中把这行数据找出来,把修改前的数据找出来写到cr块中,就构造出来一个cr块,然后B读这个cr块。cr块中有一行是undo块中读出来的,是事务提交前的信息,也就是说是事务修改前的信息,以上就是oracle构造cr块避免脏读。一个会话读到了另外一个会话未提交的事务所修改的数据叫脏读。oracle保证你读的数据永远是提交后的数据,未提交的数据你不会读到,查询到的数据块的scn小于查询时间点的scn。

2、一致性读的事务实现

2.1、用例子来说明一致性读

oracle读一个大表,这个表很大,假设是一万行(实际1万行很小),假设读这个过程很长,从8.50开始读这个表,8.55的时候这个表的最后一行被其他事务删除并提交,9.00时候oracle读完这个表,按照刚才那个分析应该是9999行,因为读到最后一个块的时候块里面的所有的行所对应的事务已经提交了,它应该会直接独处9999行,其实oracle会读出一万行,这也是读一致性的要求。

我们可以这么认为,oracle读的时候,首先是对这个表做一个快照,快照就像照一个像,接着从头到尾,读就是静止的状态。

2.2、scn时间点

oracle在8.50开始读这个表的时候,也就是指向这个select语句的时候,oracle把这个时间点记下,就是scn850,假设scn是这样的,假如8.50的时候时间静止了,这个时候保证目前所有的数据块或者要读的所有的数据块,它里面的事务槽的scn小于scn850。所以要明确select语句执行的时候先把时间点(即scn)记下来。

2.3、事务产生redo和undo

先来分析下事务,一个事务开始了,开始的时候在事务表加上一行信息,假设有两个回滚块,回滚块之间有连接指向,事务表中的这行指向该事务最新的回滚块。这个事务有可能修改很多数据块。数据块有事务槽,事务槽指向事务表,数据块事务槽的uba地址指向回滚块,在这个事务的整个执行过程中:

先有事务表,再就是回滚块、事务槽、数据行,数据行修改了就指向数据块对应的事务槽,从数据行到事务槽,从事务槽到回滚块,从事务槽再到事务表,事务表再一步步到回滚块,这整个的形成一个链庄,在这个过程中有几个关键点:

a、在这个事务所修改的所有的地方都会产生redo(redo记录buffer cache中所有buffer的改变),修改了事务表、回滚块、事务槽、数据行,修改的这个四个地方都产生redo。

b、对数据块的所有的修改都产生undo,包括事务槽,也就是当我要覆盖一个事务槽的时候,一定会做一件事情,就是把原理事务槽的信息也放到undo块里面去。

2.4、事务槽的覆盖

举例说明,假设现在对这个数据块来讲,8.50的时候我们找到这个数据块,这个时候这个数据块里面的scn840,因为这个数据块在8.40的时候做过修改并提交,在8.50这个时刻这个数据块的事务槽scn840,数据块被修改显示会产生事务表、回滚块,因为一个新的事务重新分配事务表重新分配回滚块,和原理的没有关系,修改的时候,上一个事务槽被修改了并已提交,可以覆盖,数据行因事务因是否提交标志是已提交,也可以修改了。第二个事务8.55的时候要修改这个数据块,先把事务表及回滚块准备号,接着修改数据块,要修改的数据块除了redo都产生以为,这个数据块修改的包括事务槽以及包括这个数据行都要被undo,这个时候这个事务槽信息和数据行信息都被写到回滚块里面去,这里面各个事务信息的链就会发生变化。

数据块的事务槽信息,新事务槽产生覆盖原来的事务槽,被覆盖的事务槽被写到新事务的undo块B,undo块B备份的原事务槽信息,还指向原事务的事务表。数据块新事务覆盖了原事务槽,事务信息被覆盖,对原事务表的指向被覆盖,这个指向备份在undo块B中,出现了在undo块中备份事务槽的这种指向情况。

(自己的疑问,数据块的数据行时间段内被多次修改提交,如何在回滚段种找到数据行最早的历史数据,如scn840这样的数据?数据块上所有修改全部产生undo,事务槽信息,数据行对事务槽指向,我理解为第一个事务对这个数据块的修改的事务信息及数据行指向,当第二个事务对该数据行修改时,覆盖了第一个事务的事务槽,将原先的事务信息包括uba及数据行指向备份到第二条事务的回滚块中,由此第一个事务的事务槽及数据行指向,变成了第二个事务回滚块中指向第一个事务的事务表及第一个事务的回滚块,数据行指向也在该回滚块中,多条事务修改该数据行,数据行上一条的事务信息及数据行指向都会被下一条数据的回滚块记录下来。当寻找scn840时的数据行,先在undo块中找到最近的这行修改的事务信息和uba地址,对应可以找到该次修改前的镜像,继续往前找,可以找到倒数第二次的事务信息及回滚块,再次往前找,以此类推找到需要的记录,前提是undo数据没有被覆盖掉。上面可以理解为数据行的回滚信息,在回滚段里以链状连接在一起,可以依次找出来)

2.5、事务cr读结果

8.50开始查询,当9点的时候要读这个数据块,读的时候发现这个块的事务槽scn是855,显然大于850,它就确认这个块在8.50以后做过修改,既然做过修改,就要找8.50以前的数据就要构造cr块,cr显然是数据块里的数据加undo数据,根据事务槽找到事务再找undo数据,把事务槽和数据行拿处理,在cr块里写入事务槽、数据行,构造一个cr块,undo拿出来的事务槽,这个事务槽里面的scn是840,构造的厕块里的事务槽里面的scn是840,840显然小于850,小于850的话cr块可用,这一行数据读出来。这就是读出一万行的原理所在。

3、事务的递归回滚

对一个数据块来将,根据事务槽找到这个事务,再找到回滚信息,在回滚信息里面还可以找到更老的一个事务槽,更老的事务槽又对应相关的事务,事务又找回滚,又可以找到回滚数据,在回滚数据里面又对应着一个更老的一个事务槽,再往前走,还可以一直往前走,通过这个我们可以理解一个知识点,可以这么认为,一个当前的数据块,根据回滚信息可以一直往前找,在undo数据保留时间足够长的情况下,可以找到对这个数据块所做的所有的修改过的回滚数据。

这个数据块现在是9.00,我可以找到8.55的这个数据块的状态,如果undo数据有的话,可以找到8.40、8.30、8.20、昨天的、甚至上个月的。如果undo表空间里的数据足够多的话,我从当前的数据块以当前的数据块为起点,沿着undo数据一直找的话,可以找到最早的对这个数据块修改的所有事务以及回滚数据,也就是说这个数据块此前所有的undo数据是以链状的形式串起来的。

原理就是因为一个数据块的改变都将被undo记录原镜像,不仅仅记录修改的数据行,事务槽也被记录。到目前为止记住一句话就可以了,一个数据块,根据这个数据块的undo数据,一直往前找,可以找到足够早的这个数据块的一个回滚数据。当然前提是undo数据足够大,undo表空间足够大。

4、ora-01555

8.50的时候开始读这个表,记下时间点,在8.51的时候,这个表的一行被删除提交了,8.52的时候这个数据块又被删除一行,然后提交了,8.53的时候这个数据又被删除一行并提交,8.55的时候我读这个数据块的时候,我发现这个数据块的事务槽scn853,853大于850,我需要往前构造cr块,第一步通过853的事务构造scn852的cr块,852不满足,再构造851的还不满足,再构造840的,小于scn850,这个cr构造出来可以读了。

有可能出现这种强开,事务在850开始,在855读到这个块的时候,往前构造,构造853没问题,852也能构造出来,但是再往前就构造不出来了,因为851的这个事务提交的时间比较早,它的undo数据有可能被覆盖了,851的数据没有了,也就是我要读的这个块的时候,构造cr块最早能构造到852,这个时候oracle就会报ora-01555,snapshot too old,就是需要构造出来的快照的时间点太老了,已经无法构造出来了。

造成01555的原因:

a)sql语句select执行时间太长了

b)undo表空间压力很大,导致你的undo数据被覆盖了。所以一般解决方法是增大undo表空间

5、设置undo表空间大小

metalink 262066.1建议是sql语句执行的最长时间*这段时间平均产生的undo大小

手爱着你找出最大的每秒生成的undo的大小

SELECT (SUM(undoblks) / SUM(((end_time - begin_time) * 86400))) * 8192 / 1024 / 1024 AS UPS_MB
FROM dba_hist_undostat t
where t.begin_time between
to_date('2020-11-10 00:30:00', 'yyyy-mm-dd hh24:mi:ss') and
to_date('2021-11-18 18:45:59', 'yyyy-mm-dd hh24:mi:ss')
and undoblks in
(select max(undoblks)
from dba_hist_undostat
where begin_time between
to_date('2020-11-10 00:30:00', 'yyyy-mm-dd hh24:mi:ss') and
to_date('2021-11-18 18:45:59', 'yyyy-mm-dd hh24:mi:ss'))
按最大的undoblks * undo_retention

6、undo的相关语句

查看回滚段的使用情况,哪个用户正在使用回滚段的资源
select s.username, u.name
  from v$transaction t, v$rollstat r, v$rollname u, v$session s
 where s.taddr = t.addr
   and t.xidusn = r.usn
   and r.usn = u.usn
 order by s.username;
 
检查UNDO Segment状态
select usn,xacts,rssize/1024/1024/1024,hwmsize/1024/1024/1024,shrinks from v$rollstat order by rssize;

最新文章

  1. 好像越来越觉得 SharePoint “难用”了
  2. 二叉树遍历(Binary Tree Traversal)
  3. Linux系统性能诊断工具纲要
  4. [原博客] POI系列(1)
  5. window下手动搭建 PHP+Nginx+Mysql(转)
  6. Android(java)学习笔记223:上下文的区分
  7. HDU 2222 Keywords Search (AC自动机)
  8. 跳跃表 C#
  9. ios页面跳转
  10. 深入一致性哈希(Consistent Hashing)算法原理,并附100行代码实现
  11. 1. CMake 系列 - 从零构建动态库和静态库
  12. Similarity measure
  13. Book118免费下载文档方法
  14. 第11章 拾遗4:IPv6(2)_给计算机配置IPv6地址
  15. Android Studio自定义组合控件
  16. zuul(springboot)设置静态资源代理和默认首页代码一例
  17. CentOS 使用命令设置代理
  18. Ubntu 14.04 下 开源骨架跟踪-skeltrack
  19. Struts2基本使用(一)--在项目中引入Struts2
  20. python学习笔记(六)— 模块

热门文章

  1. ajax请求头
  2. Django中获取用户IP方法
  3. Spring整合Mybatis原理
  4. DevExpress GridControl 在窗体默认皮肤下 设置其他皮肤
  5. QT个人笔记
  6. WeNet调试
  7. java double/float转BigDecimal,精度问题
  8. (jmeter笔记) websocket接口测试
  9. .Netframework下WebAPI发布在IIS方法
  10. html 1.0