Oracle关于ORA-08006和ORA-30926一点有趣的测试
Oracle关于ORA-08006和ORA-30926一点有趣的测试
两个报错的详细信息如下:
ORA-08006: specified row no longer exists
ORA-30926: unable to get a stable set of rows in the source tables/ORA-30926: 无法在源表中获得一组稳定的行
现有表zkm,根据如下一样的测试流程,表zkm是否为分区表会影响merge into报错结果为ORA-08006还是ORA-30926,非常的神奇。
16:17:25 ZKM@szceb(131)> create table zkm (id int, name varchar2(20))||16:17:54 ZKM@szceb(131)> create table zkm (id int, name varchar2(20)); 16:17:29 2 partition by list (name) || 16:17:29 3 (partition p1 values('a'), ||Table created. 16:17:29 4 partition p2 values('b') || 16:17:29 5 ) ||Elapsed: 00:00:00.02 16:17:29 6 enable row movement; || || Table created. || || Elapsed: 00:00:00.02 || 16:17:30 ZKM@szceb(131)> insert into zkm values (1, 'a'); ||16:17:58 ZKM@szceb(131)> insert into zkm values (1, 'a'); || 1 row created. ||1 row created. || Elapsed: 00:00:00.01 ||Elapsed: 00:00:00.01 16:17:32 ZKM@szceb(131)> commit; ||16:18:01 ZKM@szceb(131)> commit; || Commit complete. ||Commit complete. || Elapsed: 00:00:00.00 ||Elapsed: 00:00:00.00 16:17:34 ZKM@szceb(131)> merge into zkm ||16:18:03 ZKM@szceb(131)> merge into zkm 16:17:41 2 using (select 1 id2,'b' new_name from dual ||16:18:07 2 using (select 1 id2,'b' new_name from dual 16:17:41 3 connect by level <= 2 ||16:18:07 3 connect by level <= 2 16:17:41 4 ) u ||16:18:07 4 ) u 16:17:41 5 on (zkm.id = u.id2) ||16:18:07 5 on (zkm.id = u.id2) 16:17:41 6 when matched then ||16:18:07 6 when matched then 16:17:41 7 update set name=new_name; ||16:18:07 7 update set name=new_name; merge into zkm ||merge into zkm * || * ERROR at line 1: ||ERROR at line 1: ORA-08006: specified row no longer exists ||ORA-30926: unable to get a stable set of rows in the source tables 附: 16:22:09 ZKM@szceb(131)> select 1 id2,'b' new_name from dual 16:31:31 2 connect by level <= 2; ID2 NEW ---------- --- 1 b 1 b
从侧面也能看出来ORA-08006实际上就是表zkm匹配了u表第一行后将(1,'a')改为了(1,'b'),由于'b'已经在p2分区,这之后同时表zkm原本同一行匹配了u表第二行,但那一行已经不在p1分区因此报错ORA-08006。
而如果表zkm并非不是分区表,同样的测试方法报错确是ORA-30926: unable to get a stable set of rows in the source tables。
根据上边分析ORA-08006报错原因可以看出来,当实际源表u有多组同时匹配的行的时候,会依次根据u匹配的行先被update,而后才报错,而并非一开始就对拒绝update这个动作。
那么ORA-30926呢?
此时做一次dump表zkm那一行所在的数据块(接着右边非分区表),获取Itl的信息:
16:49:41 SYS@testdb(131)> alter system flush buffer_cache; 16:50:23 SYS@testdb(131)> conn zkm/zkm 16:50:24 ZKM@testdb(131)> select dbms_rowid.rowid_relative_fno(rowid) file_id,dbms_rowid.rowid_block_number(rowid) block_id,zkm.* from zkm; FILE_ID BLOCK_ID ID NAME ---------- ---------- ---------- ------------------------- 4 150 1 a 16:50:28 ZKM@testdb(131)> alter system dump datafile 4 block 150; 16:50:42 ZKM@testdb(131)> col value for a100 16:50:49 ZKM@testdb(131)> select value from v$diag_info where name like 'De%'; VALUE ---------------------------------------------------------------------------------------------------- /u01/app/oracle/diag/rdbms/testdb/testdb/trace/testdb_ora_11922.trc 部分 testdb_ora_11922.trc 内容: Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x0005.00c.000004bb 0x00c00c41.0157.02 --U- 1 fsc 0x0000.0018fbfc 0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000 bdba: 0x01000096 data_block_dump,data header at 0x7ffff4e4da64 =============== tsiz: 0x1f98 hsiz: 0x14 pbl: 0x7ffff4e4da64 76543210 flag=-------- ntab=1 nrow=1 frre=-1 fsbo=0x14 fseo=0x1f90 avsp=0x1f7b tosp=0x1f7b 0xe:pti[0] nrow=1 offs=0 0x12:pri[0] offs=0x1f90 block_row_dump: tab 0, row 0, @0x1f90 tl: 8 fb: --H-FL-- lb: 0x1 cc: 2 --lb:lock byte,0x1表示此数据行指向上边事务槽Itl为0x01的事务。 col 0: [ 2] c1 02 --值 1 的16进制 col 1: [ 1] 61 --值'a'的16进制 end_of_block_dump 附: 10:22:23 SYS@testdb(101)> select dump(1,1016) col0,dump('a',1016) col1 from dual; COL0 COL1 ------------------- --------------------------------------- Typ=2 Len=2: c1,2 Typ=96 Len=1 CharacterSet=ZHS16GBK: 61
接着在原来会话执行merge into,会自动在会话的trc文件产生trace信息。
如下:
对应Itl为0x02的行,其Flag列值为"----",表示当时的事务是活动的,或者在块清除前提交事务。
而col 1(即name列)的值为'b',已经是被修改的状态(当然,报错后自动回滚了)。
[root@dbtest ~]# echo '' > /u01/app/oracle/diag/rdbms/testdb/testdb/trace/testdb_ora_11922.trc 09:19:25 ZKM@testdb(131)> merge into zkm 09:19:25 2 using (select 1 id2,'b' new_name from dual 09:19:25 3 connect by level <= 2 09:19:26 4 ) u 09:19:26 5 on (zkm.id = u.id2) 09:19:26 6 when matched then 09:19:26 7 update set name=new_name; merge into zkm * ERROR at line 1: ORA-30926: unable to get a stable set of rows in the source tables 截取自动在testdb_ora_11922.trc产生的部分信息: Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x0005.00c.000004bb 0x00c00c41.0157.02 C--- 0 scn 0x0000.0018fbfc 0x02 0x0003.017.000004d0 0x00c00960.0121.12 ---- 1 fsc 0x0000.00000000 bdba: 0x01000096 data_block_dump,data header at 0xb29b0064 =============== tsiz: 0x1f98 hsiz: 0x14 pbl: 0xb29b0064 76543210 flag=-------- ntab=1 nrow=1 frre=-1 fsbo=0x14 fseo=0x1f90 avsp=0x1f7b tosp=0x1f7b 0xe:pti[0] nrow=1 offs=0 0x12:pri[0] offs=0x1f90 block_row_dump: tab 0, row 0, @0x1f90 tl: 8 fb: --H-FL-- lb: 0x2 cc: 2 --0x02的事务。 col 0: [ 2] c1 02 --值 1 col 1: [ 1] 62 --值'b' end_of_block_dump
从这可以看出,merge into即便是遇到ORA-30926后,还是会产生事务,同ORA-08006。
只不过该事务转瞬即逝,无法在视图v$transaction中查看对应信息。
不过,这里可以看自动生产的trace信息中的ITL事务,挖挖看当时产生的undo。
提取信息:
Itl Xid Uba Flag Lck Scn/Fsc 0x02 0x0003.017.000004d0 0x00c00960.0121.12 ---- 1 fsc 0x0000.00000000
Xid:
usn=0003,对应undo段序号为3
slot=017,对应事务表槽号为23(0x017转为10进制:1*16+7=23)
wrap=000004d0,对应被覆盖次数为1232(0x4d0转10进制:4*16*16+13*16=1232)
其中,usn即v$transaction.xidusn
slot即v$transaction.xidslot
wrap即v$transaction.xidsqn
undo序号3的段名为:
10:11:03 SYS@testdb(193)> select name from v$rollname where usn=3; NAME ------------------------- _SYSSMU3_2556918060$
Uba:
DBA=00c00960,回滚块地址值10进制:12585312
seq#=0121,覆盖次数289
rec#=12,块中第18行
关于DBA(Data Block Address)中包含了undo块的文件号和块地址,以下演示如何分区获取:
10:07:22 SYS@testdb(193)> --转10进制 10:07:27 SYS@testdb(193)> select to_number('00c00960','xxxxxxxx') from dual; TO_NUMBER('00C00960','XXXXXXXX') -------------------------------- 12585312 10:07:31 SYS@testdb(193)> select dbms_utility.data_block_address_file(12585312) file_id from dual; FILE_ID ---------- 3 10:07:55 SYS@testdb(193)> select dbms_utility.data_block_address_block(12585312) block_id from dual; BLOCK_ID ---------- 2400
该事务使用的undo块地址在3号文件的2400号块。
dump看看,并找到trc文件中rec#=0x12处的记录,如下:
10:12:21 SYS@testdb(193)> col value for a100 10:12:35 SYS@testdb(193)> select value from v$diag_info where name like 'De%'; VALUE ---------------------------------------------------------------------------------------------------- /u01/app/oracle/diag/rdbms/testdb/testdb/trace/testdb_ora_30168.trc 10:19:02 SYS@testdb(193)> alter system flush buffer_cache; 10:19:14 SYS@testdb(193)> alter system dump datafile 3 block 2400;
testdb_ora_30168.trc文件,对应rec#=0x12的相关记录:
*----------------------------- * Rec #0x12 slt: 0x17 objn: 88290(0x000158e2) objd: 88290 tblspc: 4(0x00000004) * Layer: 11 (Row) opc: 1 rci 0x11 Undo type: Regular undo User Undo Applied Last buffer split: No Temp Object: No Tablespace Undo: No rdba: 0x00000000 *----------------------------- KDO undo record: KTB Redo op: 0x02 ver: 0x01 compat bit: 4 (post-11) padding: 1 op: C uba: 0x00c00960.0121.11 KDO Op code: URP row dependencies Disabled xtype: XA flags: 0x00000000 bdba: 0x01000096 hdba: 0x01000092 itli: 2 ispac: 0 maxfr: 4858 tabn: 0 slot: 0(0x0) flag: 0x2c lock: 2 ckix: 0 ncol: 2 nnew: 1 size: 0 col 1: [ 1] 61 <--这里表示数据的前镜像,即'a'
看来merge into无论是ORA-08006和ORA-30926,对于多条记录还是会先被update,然后才报错。
但是,又发现另外个现象,对于源表即使有多条记录,也不一定报错,
10:49:29 ZKM@testdb(131)> select * from zkm; ID NAME ---------- ------------------------- 1 a Elapsed: 00:00:00.00 10:49:33 ZKM@testdb(131)> merge into zkm 10:49:34 2 using (select 1 id2,'b' new_name from dual 10:49:34 3 connect by level <= 2 10:49:34 4 ) u 10:49:34 5 on (zkm.id = u.id2) 10:49:34 6 when matched then 10:49:34 7 update set name='a'; 2 rows merged.
嗯。。非常的神奇。。
参考链接
https://asktom.oracle.com/ords/f?p=100:11:::::P11_QUESTION_ID:9537705900346441288
https://asktom.oracle.com/ords/f?p=100:11:705597715325014:::::
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?