首页 > 数据库 >Oracle之数据库一致性读的原理

Oracle之数据库一致性读的原理

时间:2024-05-03 16:44:05浏览次数:50  
标签:ITL SCN 记录 数据库 undo 一致性 Oracle 数据

1.概述

在Oracle数据库中,undo主要有三大作用:提供一致性读(Consistent Read)、回滚事务(Rollback
Transaction)以及实例恢复(Instance Recovery)

2.原理

一致性读是相对于脏读(Dirty Read)而言的。假设某个表T中有10000条记录,获取所有记录需要15分钟时间。当前时间为9点整,某用户A发出一条查询语句:select * from T,该语句在9点15分时执行完毕。当用户A执行该SQL语句到9点10分的时候,另外一个用户B发出了一条delete命令,将T表中的最后一条记录删除并提交了。那么到9点15分时,A用户将返回多少条记录?

如果返回9999条记录,则说明发生了脏读;如果仍然返回10000条记录,则说明发生了一致性读。很明显,在9点钟那个时间点发出查询语句时,表T中确实有10000条记录,只不过由于I/O的相对较慢,所以才会花15分钟完成所有记录的检索。对于Oracle数据库来说,没有办法实现脏读,必须提供一致性读,并且该一致性读是在没有阻塞用户的DML的前提下实现的。

那么undo数据是如何实现一致性读的呢?还是针对上面的例子。用户A在9点发出查询语句时,服务器进程会将9点那个时间点上的SCN号记录下来,假设该SCN号为SCN9.00。那么9点整的时刻的SCN9.00一定大于等于记录在所有数据块头部的ITL槽中的 SCN号(如果有多个ITL[1]槽,则为其中最大的那个SCN号)。

服务器进程在扫描表T的数据块时,会把扫描到的数据块头部的ITL槽中的SCN号与SCN9:00之间进行比较,哪个更大。如果数据块头部的SCN号比SCN9.00要小,则说明该数据块在9点以后没有被更新,可以直接读取其中的数据;否则,如果数据块ITL槽的SCN号比SCN9.00要大,则说明该数据块在9点以后被更新了,该块里的数据已经不是9点那个时间点的数据了,于是要借助undo块。

9点10分,B用户更新了表T的最后一条记录并提交(注意,在这里,提交或者不提交并不是关键,只要用户B更新了表T,用户A就会去读undo数据块)。假设被更新记录属于N号数据块。那么这个时候N号数据块头部的ITL槽的SCN号就被改为SCN9.10。当服务器进程扫描到被更新的数据块(也就是N号块)时,发现其ITL槽中的SCN9.10大于发出查询时的SCN9.00,说明该数据块在9点以后被更新了。于是服务器进程到N号块的头部,找到SCN9.10所在的ITL槽。由于ITL槽中记录了对应的undo块的地址,于是根据该地址找到undo块,将 undo块中的被修改前的数据取出,再结合N号块里的数据行,从而构建出9点10分被更新之前的那个时间点的数据块内容,这样的数据块叫做CR块(Consistent Read)。对于delete来说,其undo信息就是insert,也就是说该构建出来的CR块中就插入了被删除的那条记录。随后,服务器进程扫描该 CR块,从而返回正确的10000条记录。

让我们继续把问题复杂化。假设在9点10分B用户删除了最后一条记录并提交以后,紧跟着9点11分,C用户在同一个数据块里(也就是N号块)插入了2条记录。这个时候Oracle又是如何实现一致性读的呢(假设表T的initrans为1,也就是只有一个ITL 槽)?因为我们已经知道,事务需要使用ITL槽,只要该事务提交或回滚,该ITL槽就能够被重用。换句话说,该ITL槽里记录的已经是SCN9.11,而不是SCN9.10了。这时,ITL槽被覆盖了,Oracle的服务器进程又怎能找回最初的数据呢?

其中的秘密就在于,Oracle在记录undo数据的时候,不仅记录了改变前的数据,还记录了改变前的数据所在的数据块头部的ITL信息。因此,9点10分B用户删除记录时(位于N号块里,并假设该N号块的ITL信息为[Undo_block0 SCN8.50]),则Oracle会将改变前的数据(也就是insert)放到undo块(假设该undo块地址为Undo_block1)里,同时在该undo块里记录删除前ITL槽的信息(也就是[Undo_block0 SCN8.50])。删除记录以后,该N号块的ITL信息变为 [Undo_block1 SCN9.10];到了9点11分,C用户又在N号块里插入了两条记录,则Oracle将插入前的数据(也就是delete两条记录)放到undo块(假设该undo块的地址为Undo_block2)里,并将9点11分时的ITL槽的信息(也就是[Undo_block1 SCN9.10])也记录到该undo块里。插入两条记录以后,该N号块的ITL槽的信息改为 [Undo_block2 SCN9.11]。

那么当执行查询的服务器进程扫描到N号块时,发现SCN9.11大于SCN9.00,于是到ITL槽中指定的Undo_block2处找到该undo块。发现该undo块里记录的ITL信息为[Undo_block1 SCN9.10],其中的SCN9.10仍然大于SCN9.00,于是服务器进程继续根据ITL中记录的Undo_block1,找到该undo块。发现该undo块里记录的ITL信息为[Undo_block0 SCN8.50],这时ITL里的SCN8.50小于发出查询时的SCN9.00,说明这时undo块包含合适的undo信息,于是服务器进程不再找下去,而是将N号块、Undo_block2以及Undo_block1的数据结合起来,构建CR块。将当前N号的数据复制到CR块里,然后在CR块里先回退9点11分的事务,也就是在CR块里删除两条记录,然后再回退9点10分的事务,也就是在CR块里插入被删除的记录,从而构建出9点钟时的数据。Oracle就是这样,以层层嵌套的方式,查找整个undo块的链表,直到发现ITL槽里的SCN号小于等于发出查询时的那个SCN号为止。正常来说,当前undo块里记录的SCN号要比上一个undo块里记录的SCN号要小。

但是在查找的过程中,可能会发现当前undo块里记录的ITL槽的SCN号比上一个undo块里记录的SCN号还要大。这种情况说明由于事务被提交或回滚,导致当前找到的undo块里的数据已经被其他事务覆盖了,于是我们无法再找出小于等于发出查询时的那个时间点的SCN号,这时Oracle就会抛出一个非常经典的错误——ORA-1555,也就是snapshot too old的错误。

以上的描述可以用图来描述:

3.其他

回滚事务则是在执行DML以后,发出rollback命令撤销DML所作的变化。Oracle利用记录在ITL槽里记录的undo
块的地址找到该undo块,然后从中取出变化前的值,并放入数据块中,从而对事务所作的变化进行回滚。

实例恢复则是在SMON进程完成前滚并打开数据库以后发生。SMON进程会去查看undo segment头部(所谓头部就是undo segment里的第一个数据块)记录的事务表(每个事务在使用undo块时,首先要在该undo块所在的undo segment的头部记录一个条目,该条目里记录了该事务相关的信息,其中包括是否提交等),将其中既没有提交也没有回滚,而是在实例崩溃时被异常终止的事务全部回滚。

本文为转载,原文为:Oracle之数据库一致性读的原理


  1. ITL(Interested Transaction List)是Oracle数据块内部的一个组成部分,用来记录该块所有发生的事务,一个itl可以看作是一个记录,在一个时间,可以记录一个事务(包括提交或者未提交事务)。当然,如果这个事务已经提交,那么这个itl的位置就可以被反复使用了,因为itl类似记录,所以,有的时候也叫itl槽位。 ↩︎

标签:ITL,SCN,记录,数据库,undo,一致性,Oracle,数据
From: https://www.cnblogs.com/monkey6/p/18171347

相关文章

  • 数据库隔离级别
    1.概述数据库事务的隔离级别有4个,由低到高依次为Readuncommitted、Readcommitted、Repeatableread、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。脏读不可重复读幻读Readuncommitted√√√Readcommitted×√√Rep......
  • Oracle23ai 数据库的简单验证
    Oracle23ai数据库的简单验证背景2024年5.1期间发现有公众号再说Oracle23c使用容器方式进行安装的感觉可以试用把玩一下.简单安装dockerpullcontainer-registry.oracle.com/database/free:latest运行timedockerrun-d--nameoracle23ai-horacle23ai\--net......
  • db.create_all() 报错上下文?flask_sqlalchemy创建数据库找不到上下文?
    问题报错:RuntimeError:Workingoutsideofapplicationcontext.Thistypicallymeansthatyouattemptedtousefunctionalitythatneededthecurrentapplication.Tosolvethis,setupanapplicationcontextwithapp.app_context().Seethedocumentationform......
  • 【VMware vCenter】连接和使用vCenter Server嵌入式vPostgres数据库。
    vCenterServer早期支持内嵌(embedded)和外部(external)数据库,内嵌数据库就是vPostgres,基于VMwarePostgres数据库(PostgreSQL数据库),外部数据库用的多的是Oracle数据库和SQLServer数据库。因为早期使用内嵌的PostgreSQL数据库只能用于小型环境,比如仅支持几十台主机以及几百个虚拟机,所......
  • Oracle 区块链快速启动指南(全)
    原文:zh.annas-archive.org/md5/acadfed886db9b7419ae428193b122c0译者:飞龙协议:CCBY-NC-SA4.0前言本书的创作基于这样一种信念:我们将共同积极促进区块链技术的发展,并不断激励其他人分享他们的经验,并进一步影响其他人这样做。在本书中,我们将做以下事情:探索分布式分类账技......
  • Oracle日志记录模式 - LOGGING、FORCE LOGGING、NOLOGGING模式介绍
    一、日志记录模式(LOGGING、FORCELOGGING、NOLOGGING)内容转载自:https://www.cnblogs.com/tk-bolg/p/12867772.html1.三者的含义LOGGING:当创建一个数据库对象时将记录日志信息到联机重做日志文件。LOGGING实际上是对象的一个属性,用来表示在创建对象时是否记录REDO日志,包括......
  • Oracle中sysdate函数的个别用法举例
    1.什么是sysdate在Oracle中sysdate是一个函数,可直接调用sysdate,返回运行数据库的操作系统的当前日期时间。注意:返回的日期时间值的格式取决于NLS_DATE_FORMAT参数的值。sysdate的单位是天。2.sysdate用法举例2.1sysdatesysdate-A型,这种结果是天数,代表往前推A天;SELECT......
  • 在 .net core 3.1 的 docker 镜像生成的容器中,连接 sql server 2008 r2 版本的数据库,
    错误1:{"ClassName":"System.Data.SqlClient.SqlException","Message":"Aconnectionwassuccessfullyestablishedwiththeserver,butthenanerroroccurredduringthepre-loginhandshake.(provider:TCPProvider,......
  • Prometheus监控MongoDB数据库
    监控环境:Prometheus数据库:MongoDB3.4.6集群,3个节点监控工具:mongodb_exporter 1、创建Mongodb监控可读账号 mongodbadmin库中执行useadmindb.createUser({user:"prometheus",pwd:"prometheus",roles:[{role:"read",db:&qu......
  • ACK One x OpenKruiseGame 全球游戏服多地域一致性交付最佳实践
    作者:刘秋阳、蔡靖前言在当今全球一体化的经济环境下,数字娱乐产业正日益成为文化和商业交流的有力代表。在此背景下大量游戏厂商尝试游戏出海并取得了令人瞩目的成绩,许多游戏以全球同服架构吸引着世界各地广泛的玩家群体。游戏全球化部署不仅扩大了单个产品的市场规模,而且提高了......