本篇内容主要讲解“怎么理解Oracle事务”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么理解Oracle事务”吧!
oracle事务
1概述
事务由一个或多个DML语句组成,起始于第一条DML语句,终止于DDL或者DCL语句。可以在事务内使用SAVEPOINT命令给出控制程度。,通过事务机制确保这一组SQL语句所作的操作要么完全成功执行,完成整个工作单元操作,要么一点也不执行。
2事务的特性
3事务控制
1 COMMIT命令
许多人(甚至某些经验丰富的DBA)在提交处理这个环节上都会出现不完全或者完全错误的理解oracle体系结构的情况。执行COMMIT命令时发生的所有物理操作时LGWR进程将日志缓冲区的内容刷新到磁盘。DBWn进程完全没有执行任何操作。对于oracle数据库来说,这是一个非常重要的性能特性。
注意:执行COMMIT命令时,DBWn进程不会进行任何操作。
为了使某个事务持久,所需的全部工作是将组成这个事务的变更写入磁盘(不必使实际表数据存在于磁盘的数据文件上)。如果变更以多重重做日志文件的形式存在于磁盘上,那么在出现使数据库受损的事件时,通过从数据库受损前所做的备份中还原数据文件以及应用重做日志文件中的变更,就可以重新实例化事务。
2 ROLLBACK命令
在事务处理过程中,Oracle会保存事务处理之前数据的映像。在事务处理过程中,会将这个映像提供给查询数据的其他会话。如果出现错误,或者会话故意请求回滚,那么它也可以用来自动回滚事务。
回滚之前数据的状态是,数据已经改变,但反转这些变更所需的信息是可用的。为了满足隔离性原则,会将这些信息提供给其他所有会话。回滚会恢复数据改变之前的映像,从而抛弃所有变更;事务插入的所有行都会删除,(这里留个疑问)事务删除的所有行都会重新插入表中,已经更新的行会回到原始状态。其他会话根本不知道发生了什么,它们绝对看不到这些变更。处理事务的会话现在会将数据看做事务开始之前的数据。
3 SAVEPOINT命令
使用保存点就是允许编程人员在事务中设置一个标记,这个标记可以用来控制ROLLBACK命令的效果。除了回滚整个事务并终止它之外,还可以反转在特定点之后所做的所有变更,同时保持在该点之前所做的变更不变。事务本身继续进行:仍然没有提交,仍然可以回滚,仍然对其他会话不可见。
3.1使用savepoint的规则:
(1)所有的savepoint语句都必须包含一个名称。在后台,您所创建的savepoint名称会与"系统变更号"(system change number,scn)"相关联。这是savepoint所标注的对象。
(2)在一个事务中不应当重复savepoint的名称,事务是以提交事件作为结束的一连串的SQL语句。如果重复一个名称,那么不会看到语法错误或执行错误。相反,新的savepoint会覆盖较早的savepoint,从效果上是将之前的存储点删除了。
(3)一旦提交事件发生,无论是显示或隐式提交事件,则所有现有的存储点都将会从内存中被删除。
4 SELECT FOR UPDATE
最后一个事务控制语句是SELECT FOR UPDATE。在默认情况下,Oracle提供最高级别的并发性:读者不打断写者,写者也不打断读者。或者简单的说,一个会话查询另一个会话正在更新的数据,或者一个会话更新另一个会话正在查询的数据,这都没有问题。然而,有时需要改变这种行为,防止改变正在被查询的数据。
应用程序使用SELECT命令检索一组行,将它们提供给用户精读,并给用户提示所做的变更,这种情况不是不常见。因为ORACLE是一个多用户数据库,所以另一个会话也要检索这些行也不是不可能。如果这两个会话都要做出变更,那么会出现一些奇怪的效果。
Session1 | Session2 | |
select * from emp; 出现N条数据,其中包括id=1的数据 | ||
用户删除emp表一条数据 delete from emp where id=1; | ||
update emp set name='test' where id=1; 此时会出现"0 rows updated" | ||
解决这个问题的方法之一就是锁定用户感兴趣的行: select * from emp for update; |
FOR UPDATE子句会锁定所有检索的行。除了发出命令的会话之外,其他任何会话都不能改变它们,因此后面的更新操作就会成功。这意味着一个会话有一致的数据视图(不会改变),但付出的代价是如果其他会话要更新锁定的行,它们就会挂起(当天他们可以查询)。
在发出命令的会话发出COMMIT或ROLLBACK命令之前,会一直保持FOR UPDATE子句设置的锁定。必须这样来释放锁定,即使没有执行DML命令。
5 所谓的"自动提交"
在结束对提交处理的讨论之前,我们有必要阐明一下经常被提及的"自动提交"(有时候也被称为隐式提交)。您经常会听到这样的说法:Oracle在某些情况下可以进行"自动提交"。
执行DDL语句就是其中一种情况,
退出某个用户进程(SQL*Plus)则是另一种情况。
"自动提交"纯属子虚乌有。执行某条DDL语句时,实现这个DDL命令的源代码包含了一个完全正规的COMMIT命令。但是,退出用户进程时的情况是怎样呢?
执行"退出"命令(正常) | 直接点击右上角关闭(异常) | |
Windows | 提交 | 回滚 |
Linux | 提交 | 回滚 |
如果在WINDOWS终端上使用SQL*Plus并执行一条DML语句,然后再执行"退出"命令,就会提交事务。这是因为SQL*Plus中的"退出"命令嵌入了一条COMMIT语句。
但是,单击SQL*Plus窗口的右上角此时,关闭SQL*Plus窗口,如果再次登录SQL*Plus,您会发现已回滚了事务。这是因为为Microsoft Windows编写SQL*Plus的编程人员在关闭SQL*Plus窗口的代码中嵌入了一条ROLLBACK语句。
SQL*Plusz在其他平台上的行为可能有所不同,唯一的确认方法是测试。因此,以不同方式退出某个程序时是否能够进行"自动提交"完全取决于编程人员如果编写用户进程。Oracle服务器只是按照指令进行操作。
4 oracle提交数据的类型
1 显式提交
用COMMIT命令直接完成的提交为显式提交。
其格式为:SQL>COMMIT;
2 隐式提交
用SQL命令间接完成的提交为隐式提交
这些命令是:ALTER,AUDIT,COMMENT,CONNECT,CREATE,DISCONNECT,DROP,EXIT,GRANT,NOAUDIT,QUIT,REVOKE,RENAME。 执行这些命令前oracle会执行一次commit,命令执行完后oracle也会自动执行一次commit
3 自动提交
若把AUTOCOMMIT设置为ON,则在插入、修改、删除语句执行后,系统将自动进行提交,这就是自动提交。 其格式为:SQL>SET AUTOCOMMIT ON; 它只是DML语句执行后的自动提交,对会话的正常退出或异常退出都没有作用。
这个参数属于客户端sqlplus,oracle自身并没有这个参数
查看这个参数的值:
SHOW AUTOCOMMIT
设置这个参数的值:
SET AUTO[COMMIT] { OFF | ON | IMM[EDIATE] | n }
5 ITL(事务槽)
1 ITL描述
ITL(Interested Transaction List)是Oracle数据块内部的一个组成部分,位于数据块头(block header),
每一个oracle块的头部都有事务槽,oracle块里有一个PCT_free的概念,即oracle会预留块大小的10%作为缓冲,当修改oracle的事务增加时,事务槽向下增长,当更新oracle块的数据时,数据向上增长,PCT_free的空间被压缩。当PCT_free被用完时,oracle就彻底填满了,如果还有事务要修改Oracle块,就需要在事务队列中等待这叫做事务槽的争用。
itl由xid,uba,flag,lck和scn/fsc组成,用来记录该块所有发生的事务,一个itl可以看作是一条事务记录。当然,如果这个事务已经提交,那么这个itl的位置就可以被反复使用了,因为itl类似记录,所以,有的时候也叫itl槽位。如果一个事务一直没有提交,那么,这个事务将一直占用一个itl槽位,itl里面记录了事务信息,回滚段的入口,事务类型等等。如果这个事务已经提交,那么,itl槽位中还保存的有这个事务提交时候的SCN号。
dump一个数据块可以在事务区看到ITL信息
Block header dump: 0x01001543 Object id on Block? Y seg/obj: 0x15c5a csc: 0x00.1ce6af itc: 3 flg: E typ: 1 - DATA brn: 0 bdba: 0x1001540 ver: 0x01 opc: 0 inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc 0x01 0xffff.000.00000000 0x00000000.0000.00 C--- 0 scn 0x0000.001ce6af 0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000 0x03 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000 bdba: 0x01001543 |
Object id on Block? Y是表示是否是对象的块
0x01001543是数据文件号块号
seg/obj: 0x15c5a是对象id 89178,它是rowid的前6个64进制字符AAAVxa的16进制形式,在dump(rowid,16)中的前4位中也可以看出,可参考:http://blog.itpub.net/28539951/viewspace-1986647/
csc: 0x00.1ce6af是The cleanout SCN that is used during read consistency
itc: 3是块中itl slot的数量 flg: E E是使用ASSM O是使用free list
typ: 1 - DATA 1是数据,2是索引
brn: 0
bdba: 0x1001540是Block relative data block address
ver: 0x01
opc: 0 inc: 0 exflg: 0
Itl是块上相关事务列表interested transaction list 每条记录中的行级锁对应于Itl列表中的序号,即哪个事务在该记录上产生的锁
Xid是事务id.Xid=Undo Segment Number XIDUSN+Transaction Table Slot Number XIDSLOT+ Wrap XID = usn#.slot#.warp#
Uba是该事务对应的回滚段地址.Uba=回滚块地址(undo文件号UBAFIL和数据块号UBABLK)+回滚序列号UBASQN+回滚记录号UBAREC
Flag是事务标志位 ---= 事务是活动的,或者在块清除前提交事务 C =事务已经提交并且清除了行锁定 B = this undo record contains the undo for this ITL entry U =事务已经提交(SCN已经是最大值),但是锁定还没有清除(快速清除) T = transaction was still active at block cleanout SCN
Lck是这个事务影响的行数
Scn/Fsc是scn或者free space credit |
相关实验可以参考
https://blog.csdn.net/gumengkai/article/details/63684545
2事务槽参数
每个块都有一个块首部。这个块首部中有一个事务表。事务表中会建立一些条目来描述哪些事务将块上的哪些行/元素锁定。这个事务表的初始大小由对象的INITRANS 设置指定。对于表,这个值默认为2(索引的INITRANS 也默认为2)。事务表会根据需要动态扩展,最大达到MAXTRANS 个条目(假设块上有足够的自由空间)。所分配的每个事务条目需要占用块首部中的23~24 字节的存储空间。注意,对于Oracle 10g,MAXTRANS 则会忽略,所有段的MAXTRANS 都是255。
设initrans值为2,则数据库服务器在一个数据块中最多有两个并行的事务可以独立、并行的通过自己的事务槽,实现对共享数据块中的行数据的事务操作。
也就是说,如果某个事物锁定了这个块的数据,则会在这个地方记录事务的标识,当然那个事务要先看一下这个地方是不是已经有人占用了,如果有,则去看看那个事务是否为活动状态。如果不活动,比如已经提交或者回滚,则可以覆盖这个地方。如果活动,则需要等待(闩的作用)。所以,如果有大量的并发访问使用的这个块,则参数不能太小,否则资源竞争将导致系统并发性能下降。
ORACLE 并发insert事务的时候的块分配和ITL管理
INITRANS =1 时 并发多个INSERT 事务(本次测试最多5个)的时候并不会由于ITL的争用而等待组塞,ORACLE 采取的策略是每个INSERT事物分配不同的一些块来使用,这样各个会话之间就不会产生冲突,除非段没有多余的块。
创建一张表,并查看建表的参数 create table t1(id int,num int); select TABLE_NAME,STATUS,PCT_FREE,PCT_USED,INI_TRANS,MAX_TRANS,INITIAL_EXTENT,NEXT_EXTENT,MIN_EXTENTS,MAX_EXTENTS,PCT_INCREASE from user_tables where table_name='T1'; |
插入数据 insert into t1 values(1,1); insert into t1 values(2,1); insert into t1 values(3,1); insert into t1 values(4,1); commit; |
查看表的数据的分布 select ID,num ,dbms_rowid.rowid_relative_fno(rowid) file#, dbms_rowid.rowid_block_number(rowid) block# from T1; |
会话1 插入数据查看,发现插入到4号文件的532号块 insert into t1 values(5,1); select ID,num ,dbms_rowid.rowid_relative_fno(rowid) file#, dbms_rowid.rowid_block_number(rowid) block# from T1; |
会话2 插入数据查看,发现插入到4号文件的533号块 insert into t1 values(6,1); select ID,num ,dbms_rowid.rowid_relative_fno(rowid) file#, dbms_rowid.rowid_block_number(rowid) block# from T1; |
会话2 插入数据查看,发现插入到4号文件的535号块 insert into t1 values(9,9); select ID,num ,dbms_rowid.rowid_relative_fno(rowid) file#, dbms_rowid.rowid_block_number(rowid) block# from T1; 综上所述INSERT事物分配不同的一些块来使用,这样各个会话之间就不会产生冲突,除非段没有多余的块 |
ORACLE 并发update事务的时候的块分配和ITL管理
INITRANS =1 时 并发多个UPDATE事务(本次测试最多7个)的时候也不会由于ITL的争用而导致等待产生,此时ORACLE除了使用默认的ITL之外,另外动态扩展所需要的ITL,紧紧在非常极端的情况下才会出现等待。
1) 该BLOCK没有FREE空间了,注意FREE参数的设置不能太小。
2) 该块使用的ITL总数,超过该块允许的ITL的最大值min(round(block_size*0.5/24) - 2 ,255) 。
要达到这样的极端情况实际的生产情况是很难的,应该比业务SQL的死锁出现的概率更小。
3 ITL等待
发生等待的场景:
1.超过maxtrans配置的最大ITL数
2.initrans不足,没有足够的free space来扩展ITL
解决方法:
maxtrans不足:
这一情况是由高并发引起的:同一数据块上的事务量已经超出了其实际允许的ITL数。因此,要解决这类问题就需要从应用着手,减少事务的并发量;长事务,在保证数据完整性的前提下,增加commit的频率,修改为短事务,减少资源占用事件。而对于OLAP系统来说(例如,其存在高并发量的数据录入模块),可以考虑增大数据块大小。
initrans不足:
数据块上的ITL数量并没有达到MAX TRANS的限制,发生这种情况的表通常会被经常UPDATE,从而造成预留空间(PCTFREE)被填满。如果我们发现这类ITL等待对系统已经造成影响,可以通过增加表的INITRANS或者PCTFREE来解决(视该表上的并发事务量而定,通常,如果并发量高,建议优先增加INITRANS,反之,则优先考虑增加PCTFREE)。
要注意的一点是,如果是使用ALTER TABLE的方式修改这2个参数的话,只会影响新的数据块,而不会改变已有数据的数据块——要做的这一点,需要将数据导出/导入、重建表。
Oracle事务相关概念
1 事务id
每一个事物都有一个自己的事物ID,就像身份证号一样。
在v$transaction数据字典中xid就是事物ID,xid既是一个编号,也是一个地址,xid中内容的有
1、使用哪个回滚段的段头块;XIDUSN
2、一个undo段最多同时能有47个活动事物,一个undo段只有一个事物表,47个事物的情况都在事物表中 ,一个事物占用一行,此次事物使用47中的哪一行;XIDSLOT
3、该行被覆盖的次数。XIDSQN
select xid,xidusn,xidslot,xidsqn,start_time,start_scnb,used_ublk,used_urec,log_io,phy_io from v$transaction;
注:过一段时间在查询,若used_urec 字段不断增加,说明该事物正在继续,如果该字段不断下降,说明该事物正在回滚
联合v$session 再查一次,这样就知道是哪个用户的哪些事务了并且获取sid
select b.sid,b.username,xid,a.status,start_time,used_ublk,used_urec,log_io,phy_io from v$transaction a,v$session b where a.ses_addr = b.saddr;
获得sid这样就知道是哪个用户的哪些事务了,进而可以跟踪到sql
select sql_text from v$sqlarea a,v$session b where a.SQL_ID=b.PREV_SQL_ID and b.SID=&sid;
V$TRANSACTION各列字段的含意
列名 数据类型 含义 ADDR RAW(4|8) Address of the transaction state object XIDUSN NUMBER 使用的回滚段号,可以和v$rollstat对应 XIDSLOT NUMBER RBS TX 表中的槽号 USN.SLOT.SQN XIDSQN NUMBER 序列号,slot被重复使用的次数TX-USNxSLOT-SQNxxxxx UBAFIL NUMBER 最后一个撤销块地址所在的文件号 UBABLK NUMBER UBA 块号 UBASQN NUMBER UBA 序列号 UBAREC NUMBER UBA 记录号 STATUS VARCHAR2(16) 当前事务的状态 START_TIME VARCHAR2(20) Start time (wall clock) START_SCNB NUMBER 开始的系统改变号 START_SCNW NUMBER Start SCN wrap START_UEXT NUMBER Start extent number START_UBAFIL NUMBER Start UBA file number START_UBABLK NUMBER Start UBA block number START_UBASQN NUMBER Start UBA sequence number START_UBAREC NUMBER Start UBA record number SES_ADDR RAW(4|8) 用户会话对象地址,对v$session 的saddr列 FLAG NUMBER Flag SPACE VARCHAR2(3) YES if a space transaction RECURSIVE VARCHAR2(3) YES if a recursive transaction NOUNDO VARCHAR2(3) YES if a no undo transaction PTX VARCHAR 2(3) YES if parallel transaction NAME VARCHAR2(256) Name of a named transaction PRV_XIDUSN NUMBER Previous transaction undo segment number PRV_XIDSLT NUMBER Previous transaction slot number PRV_XIDSQN NUMBER Previous transaction sequence number PTX_XIDUSN NUMBER Rollback segment number of the parent XID PTX_XIDSLT NUMBER Slot number of the parent XID PTX_XIDSQN NUMBER Sequence number of the parent XID DSCN-B NUMBER This column is obsolete and maintained for backward compatibility. The value of this column is always equal to the value in DSCN_BASE. DSCN-W NUMBER This column is obsolete and maintained for backward compatibility. The value of this column is always equal to the value in DSCN_WRAP. USED_UBLK NUMBER 已占用的undo 块数 USED_UREC NUMBER 已使用的undo 记录数 LOG_IO NUMBER 逻辑 I/O PHY_IO NUMBER 物理 I/O CR_GET NUMBER 一致读的次数 CR_CHANGE NUMBER Consistent changes START_DATE DATE Start time (wall clock) DSCN_BASE NUMBER Dependent SCN base DSCN_WRAP NUMBER Dependent SCN wrap START_SCN NUMBER Start SCN DEPENDENT_SCN NUMBER Dependent SCN XID RAW(8) Transaction XID PRV_XID RAW(8) Previous transaction XID PTX_XID RAW(8) Parent transaction XID |
通过xid获取usn/slot/sqn方式如下:
with v as(select '0200010068050000' xid from dual) select to_number(substr(v.xid,3,2)||substr(v.xid,1,2),'xxxx') usn, to_number(substr(v.xid,7,2)||substr(v.xid,5,2),'xxxx') slot, to_number(substr(v.xid,15,6)||substr(v.xid,13,2)||substr(v.xid,11,2)||substr(v.xid,9,2),'xxxxxxxxxx') sqn from v; |
2 事务表
undo表空间的undo段的第一个数据块(即undo段的段头块)里放事物表,共有47行。事物开始第一件事就是在事物表中找到一个空行,写上事物信息。也就是说undo段最多47个活动事物,但是oracle会尽量将一个事物放到一个段上,为了均匀分配(undo段的段头块的位置在dba_segments表空间可以查得)。
事务管理起始于undo段,并以此为中心。undo段的第一个块(段头块)包含如下结构:扩展映射,扩展控制头(跟其他类型的段头块一样),事务表,事务控制区(特殊的结构)。事务表的大概结构如下:
dump一个undo段的一个块可以看到事务表
index 表示事务表中槽号,只是一个序列而已,从0x00开始到0x21结束,11g的版本有34个槽。
state 表示事务状态:9代表事务不活动,10代表事务正在活动,从这里我们看出16进制第0x17号槽上的事务正在活动。大家有没有发现,我们在发生事务前,Oracle会找事务控制列表中的chd=0x0017,说白了就是重从index=0x17的槽,存放当前最新的事务:
注:下面的事务控制,是我在发生事务前(即做update gyj_test set name='GGGGG' where id=1;前所DUMP的事务控制)
TRN CTL:: seq: 0x000d chd: 0x0017 ctl: 0x000b inc: 0x00000000 nfb:0x0001
mgc: 0xb000 xts: 0x0068 flg: 0x0001 opt: 2147483646 (0x7ffffffe)
uba: 0x0280000a.000d.2b scn: 0x0000.0028a26a
cflags 表示正在使用穿上事务槽的事务的状态:0x00表示非活动事务、0x80表示活动事务、0x10表示死事务、0x90表示被回滚的死事务
平时我们看到的最多就是0x00表示非活动事务、0x80表示活动事务,后面的很少发生。
wrap# 表示事务表上的事务槽被重用的次数,它是XID的一部分。0x001d表示此时事务槽被重用了29次。
uel 表示当前活动事务所在事务槽的下一个事务槽的指针(即如果又发生一个新的事务,此时就会用到UEL指向的事务槽上的index)。
scn 表示务事启动、提交、回滚的SCN.
dba 表示uba:第一部分的undo块地址,这个DBA是(rollback)回滚的起始点,也就是说是记录事务修改的最后一条记录所在UNDO块的地址。
nub 表示当前事务所用到的UNDO块的个数。
cmt 表示最接近当前的提交时间戳,是从1970年1月1号零晨开始的(以秒为单位记录)。0表示事务正在活动。 |
oracle中有哪些undo段:
select * from v$rollname; |
3 事物槽
在每个数据块的块头部分有事物槽,事物槽包括xid、uba(undo block address)等等
当事物发生的时候,
第一件事会在undo表空间的相对空闲的undo段的段头块的事物表中找到一个槽位,写上事物信息(xid),给这个事物分配一个undo块(undo块里写的就是修改之前的数据),然后将undo块的地址(即uba写到事物表中),所以现在事物表中有xid和uba;
第二件事在要修改的数据块的块头的事物槽中找到槽位,写上事物信息(xid),(目的是通过数据块上的xid可以找到事物表),然后在将要修改的数据块上修改数据,修改之前的信息写到undo块里。同时,在事物槽中也写上uba地址,指向回滚块。为什么要在两个地方写事物信息呢?下面有解释。
当回滚块的数据写满之后,系统会自动分配一个回滚块,比如该事物修改了较多的数据,产生3个undo块,3个undo块有先后关系,它们会链起来,但是这是事物表中的uba就只是指向最新的undo块,这是便于回滚。
数据块的事物槽中的uba指向回滚数据,这便于构造CR块。
一个事物一个事物槽,只有当事物提交了,该事物槽才能被覆盖。
pctfree:1、当执行更新操作时,也许会占用pctfree的空间。2、当多个事物操作该数据块时,需要增加事物槽的数量,也会占用pctfree,但是如果事物过多,pctfree不够用了,(之前的事物槽未提交,就不能覆盖,该数据块又有新的事物产生,需要新的事物槽)这样就会产生事物槽争用的情况,大多数发生在update和delete的情况,insert不会发生。因为oracle会尽量的将insert插入的数据插入到多个块中,也就是平均一下,但是update和delete就无能为力了,因为这两个操作往往是针对某一行进行的,而某一行特定就是在这个块里。
到此,相信大家对“怎么理解Oracle事务”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。