背景
今天上午在查询行锁的事后发现v$lock的id1和id2,阻塞的和被阻塞的会话一样,这能说明什么?
既然是被阻塞了,那争用的应该是同一块数据,但是一个事务已经修改了,没提交数据块上还有前镜像的指针,另一个事务想要修改(这里应该修改不了那为什么id1和id2一样呢),应该是构造一致性读去读取这个地址吧?
这里计一个todo,先来看看根据行锁定位到具体是哪一个表的行数据产生了行锁。
首先id1和id2其实是和 v$trasaction 的字段相对应的
id1=xidusn+xidslot(回滚段号+回滚槽号)
id2=xidsqn(回滚槽号的覆盖次数)
后面说计算,这三个记录在数据块的 ITL,相对应的还有被修改数据的前镜像uba(回滚段地址)
uba在v$trasaction也有记录,
uba=回滚块地址(文件号+块号)+ 回滚序列号 + 回滚记录号
总结一下就是当前事务的三个xid、四个uba 和上面一一对应记录在v$trasaction
select xidusn,xidslot,xidsqn,ubafil,ubablk,ubasqn,ubarec from v$transaction;
思路:事务记录在块的ITL,尝试根据v$trasaction的回滚段号和块号找到发生行锁的块地址。
环境
Oracle11g
sql
SYS@orcl> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
CORE 11.2.0.4.0 Production
TNS for Linux: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production
模拟行锁
这里用本地的一个测试表demo01数据如下:
构造行锁,修改id=2的行数据id:2 name:test2 sex:男
接下来假装不知道是这里发生的阻塞,去这行数据
会话一:
sql
SYS@orcl> update demo01 set sex='男' where id=2;
1 row updated.
SYS@orcl>
会话二
我们知道的情况下就好办了,直接找到数据块,dump下来
sql
select rowid,
dbms_rowid.rowid_relative_fno(rowid) rel_fno,
dbms_rowid.rowid_block_number(rowid) blockno
from demo01
where id = 2;
实验
查找行锁信息
sql
select sn.username,
sn.LAST_CALL_ET,
sn.COMMAND,
sn.STATUS,
m.sid,
sn.serial#,
sn.sql_id,
m.type,
decode(m.lmode,
0,
'none',
1,
'null',
2,
'rowshare',
3,
'rowexcl.',
4,
'share',
5,
'srowexcl.',
6,
'exclusive',
lmode,
ltrim(to_char(lmode, '990'))) lmode,
decode(m.request,
0,
'none',
1,
'null',
2,
'rowshare',
3,
'rowexcl.',
4,
'share',
5,
'srowexcl.',
6,
'exclusive',
request,
ltrim(to_char(m.request, '990'))) request,
m.id1,
m.id2
from v$session sn, v$lock m
where (sn.sid = m.sid and m.request != 0) --存在锁请求,即被阻塞
or (sn.sid = m.sid --不存在锁请求,但是锁定的对象被其他会话请求锁定
and m.request = 0 and lmode != 4 and
(id1, id2) in (select s.id1, s.id2
from v$lock s
where request != 0
and s.id1 = m.id1
and s.id2 = m.id2))
order by id1, id2, m.request;
可以看到sid=427的会话在2024-12-05 10:50:42登录一直处于INACTIVE, LAST_CALL_ET:9772s,发生阻塞的数据在demo01
根据阻塞会话的 sid 就能找到v$trasaction ,有xid信息,undo 信息ubafile ubablk
sql
select
s.sid
, s.serial#
, s.username
, t.addr taddr
, s.saddr ses_addr
, t.used_ublk
, t.used_urec
-- , t.start_time
, to_char(t.flag, 'XXXXXXXX') "0xFLAG"
, t.status||CASE WHEN BITAND(t.flag,128) = 128 THEN ' ROLLING BACK' END status
, t.start_date
, XIDUSN
, XIDSLOT
, XIDSQN
, t.xid
, t.prv_xid
, t.ptx_xid
from
v$session s
, v$transaction t
where
s.saddr = t.ses_addr and sid=427;
id2=2070=xidsqn
id1=262163转成十六进制4 0013
高位4=xidusn,低位0013(十六进制)=xidslot=19(十进制)
又可以得到uba
select * from v$transaction where xidusn=4 and xidslot=19 and xidsqn=2070;
uba=回滚块地址(文件号+块号)+ 回滚序列号 + 回滚记录号
3号文件 4364块
dump undo块
sql
SYS@orcl> alter system dump datafile 3 block 4364;
System altered.
SYS@orcl> exec get_trace_name
/u01/app/oracle/diag/rdbms/orcl/orcl/trace\orcl_ora_5296.trc
PL/SQL procedure successfully completed.
dump undo 段头
根据上面的xid与v$rollname的usn关联得到段头
sql
SYS@orcl> select name from v$rollname where usn=4;
NAME
------------------------------
_SYSSMU4_1254879796$
dump undo段头
sql
SYS@orcl> ALTER SYSTEM DUMP UNDO BLOCK '_SYSSMU4_1254879796$' XID 4 19 2070;
System altered.
SYS@orcl> exec get_trace_name
/u01/app/oracle/diag/rdbms/orcl/orcl/trace\orcl_ora_1484.trc
PL/SQL procedure successfully completed.
--bdba 表示block address hdba 代表sgment header address
--当中 bdba 表示file 4,block 94,449 , hdba 表示file 4。block 94,448
block address:
sgment header address:
找到块地址之后就可以dump数据块了
dump 数据块 <<找到行锁的数据
sql
SYS@orcl> alter system dump datafile 1 block 94449;
System altered.
SYS@orcl>
SYS@orcl> exec get_trace_name
/u01/app/oracle/diag/rdbms/orcl/orcl/trace\orcl_ora_6708.trc
PL/SQL procedure successfully completed.
SYS@orcl>
得到dump的块,想得到被锁住的行数据 一是在数据区找到真正的数据(还没学会bbed)二是在尾区找,我们来找一下:
dump的一段内容:
block_row_dump:
tab 0, row 0, @0x1c57 ----第一个表第一行的位置 ,定义了该表在行索引中的起始插槽号
tl: 16 fb: --H-FL-- lb: 0x0 cc: 3 ----行头,tl 表示行的总长度(total length)这里是 8 个节。
--fb 表示行的标志字节(flag byte),用来标识行的状态。D表示被删除,这个之前做过实验Oracle delete删除数据是否为逻辑删除、新插入数据占用的数据块位置实验 - 墨天轮
--cc:3 -表示有列数,这个表有三个字段。
col 0: [ 2] c1 02 --第一行的第一个字段长度和值
--表示第 0 列的内容是一个长度为 2 的值,十六进制为 c1 02
col 1: [ 5] 74 65 73 74 31 --同理第一行的第二个字段长度和值
--表示第 1 列的内容是一个长度为 5 的值,十六进制为 74 65 73 74 31
col 2: [ 3] e5 a5 b3
tab 0, row 1, @0x1c67
tl: 16 fb: --H-FL--lb: 0x2 cc: 3 <<< ----lb: 0x1 说明事物在该数据行上的锁还没清除,并且该锁指向 02 号事物槽如下图。
col 0: [ 2] c1 03
col 1: [ 5] 74 65 73 74 32
col 2: [ 3] e7 94 b7
通过UTL_RAW转化一下,就可以看到被锁住的表的行数据了
有意思,非常有意思,实验室参考下面的文章做的,刚吸收一点点,我在去学习一下。