当前位置: 首页 > Oracle, oracle 11g > 正文

同一个rowid就一定是同一条记录吗

我小姨子在使用logmnr挖掘日志的时候,发现几条很奇怪的操作,操作时间一样,SQL语句也一模一样的DELETE操作:

2017090800001

 

如上图,分别在7:04、9:25和9:40对同一个ROWID进行的DELETE操作,而且每个时间点都发生两次操作。这样她的疑问就来了,同一时间会对同一条记录删除两次吗?

我给她的解释是,她查询显示的时间是不准的,显示的时间是DATE格式的,而那个字段是TIMESTAMP格式的,如果点开这个字段后的倒三角按钮,就会发现,看着时间一样的两条记录,时间完全是不同的。而且看着时间一样的两条记录的SCN字段也肯定是不同的,经她确认,的确是不同的。

她的疑问又来了,比如上图最上面两行时间看着一样的记录(7:04:48),如果第一条记录是由A会话操作的,第二条记录是由B会话操作的,上图显示是按时间排序的,也就是A会话的操作要早于B会话,那么如果这两个操作是同时进行的话,B会话会被A会话阻塞,也就是A会话提交完之后,B会话才能做这个DELETE操作,可是A会话完成后,这条记录已经被删掉了,B会话怎么也删除掉这条记录了呢?

于是我问她,谁说这两个操作删除的是同一条记录?她很疑惑的说,ROWID一样,不就是同一条记录吗?当然不是了,那只是很多情况下,ROWID没有发生变化,比如一些静止的数据(只读的历史表),往往同一个ROWID就能确定是同一条记录。但是,生产数据库,表的数据一直在发生变化,就无法避免会发生ROWID和对应数据的变化,比如ALTER TABLE MOVE操作、行迁移、可回收空间复写操作都会导致ROWID和对应数据的变化。

ALTER TABLE MOVE操作是用来挪动表的位置的操作,可以使用这个命令将表迁移到其他表空间,更多情况下这个操作会用来清理碎片使用。无论是迁移表还是回收碎片,数据的位置都发生了变化,ROWID肯定发生了变化。

行迁移通常是由于某条记录,在修改后查超过了PCT USED或者PCT FREE设置的值,通常是VARHAR2类型的字段,比如某个表存在一个VARCHAR2(200)长度的字段,有一条记录这个字段只存了10个字节的数据,后来通过UPDATE操作将这个字段修改到200字节,修改后,这条记录所在的数据块存不下这条记录了,通常这种情况下,这条记录就会被迁移到其他空间足够的数据块上,这时候ROWID就会发生变化。

可回收空间复写操作,通常是DELETE操作删除数据后,这些被删除的数据所在的数据空间就会被标记为可用(可回收)空间,这样如果有INSERT操作的话,就有可能把数据插入到这部分可回收的空间,这时候ROWID还是这个ROWID,可是数据却发生了变化。

很多人都知道ROWNUM是随机的生成的,对不同的会话来说,查询出来的ROWNUM可能是不一样的,所以大多数情况下ROWNUM不能用来定位数据。而ROWID是真实存在的,也有开发人员开发的程序使用ROWID对数据进行操作,这样速度会非常快,但是这样就好比使用MAX(ID)+1这种方式来实现自增长一样,都是存在问题的,只是遇到问题的概率有多大而已,因为ALTER TABLE MOVE会很少操作,有的数据库可能一生都没有做过这个操作,而行迁移也相对来说很少发生,而且对用户来讲是透明的,基本发生了也不知道,可回收空间复写相对来讲发生的概率就很高了,只是用户并不知道什么时候复写了,一般来讲,如果表空间充足,可以为表分配足够的EXTENT的时候,有数据INSERT进来的时候,通常不会使用这部分可回收的空间,这也是为什么DELETE操作很频繁的表会越来越大,碎片越来越多的原因,但当表空间没有为表分配EXTENT所需的足够空间时,这部分可回收空间就会被使用。或者当有大批量数据被插入到这张表的时候,为表分配EXTENT的速度满足不了数据插入的速度时,也会使用这部分可回收的空间,下面进行下简单的测试。

首先建一张测试表T_TEST,包含两个字段ID和NAME,向表里插入几条ID不一样的记录(1,2,3,4),这部分操作本文就不记录了,然后随便查出来一条记录的ROWID,并记录下来。

dbdream@IVLDB> SELECT MIN(ROWID) FROM T_TEST;

     ROWID
-------------------
AABzAwAAFAAAACCAAA

查询出这个ROWID对于的数据,并记录下来。

dbdream@IVLDB> SELECT * FROM T_TEST WHERE ROWID='AABzAwAAFAAAACCAAA';

        ID NAME
---------- ----------
         3 C

然后大量执行insert into T_TEST select * from T_TEST操作,将这张表的数据重复插入到这张表里。

dbdream@IVLDB> insert into T_TEST select * from T_TEST;

1572864 rows created.

dbdream@IVLDB> commit;

Commit complete.

然后删除这张表里ID等于3的所有数据,这样即会释放很多的空间,而且再插入数据时也排除了这个ID的干扰。

dbdream@IVLDB> DELETE FROM T_TEST WHERE ID=3;

1048576 rows deleted.

dbdream@IVLDB> COMMIT;

Commit complete.

然后再复制一次数据,因为这时的表已经很大了,再扩展时一次会分配多个EXTENT,这样就可能会用到上面删除掉的那部分可回收空间了。

dbdream@IVLDB> insert into T_TEST select * from T_TEST;

2097152 rows created.

dbdream@IVLDB> commit;

Commit complete.

再次查询那个ROWID,看下是否能查询到数据,如果查询不到数据,说明这部分可回收空间的这个数据块没有被使用。

dbdream@IVLDB> select * from T_TEST WHERE ROWID='AABzAwAAFAAAACCAAA';

        ID NAME
---------- ----------
         1 A

可见,这部分空间已经被复写了,存放是数据已经发生了变化,ROWID还是那个ROWID,但是数据已经不是那条数据了。这就证明了ROWID相同,并不一定代表就是同一条数据。

而上文我小姨子遇到的问题,在同一秒钟之内,就遇到了这种可回收空间复写的情况,如果他们是使用ROWID对数据进行操作,很可能就遇到问题了。

比如A和B都要删除这条记录,在7:04:48:01的时候,A和B会话都查询到了这条记录的ROWID,7:04:48:02的时候A删除了这条记录,而在7:04:48:03的时候C又在这个空间里插入了一条新记录,7:04:48:04的时候B用这个ROWID删除了这条记录,结果就把C插入的记录给删除掉了,而C插入的这条记录,很可能是有用的数据,而不是B真正要删除的数据,这样就出问题了,因为B并不知道他删除的数据是什么样的,B还以为自己删除的对。

用ROWID操作数据,效率提高的同时,也带来了一定的风险,需谨慎使用啊。

本文固定链接: http://www.dbdream.com.cn/2017/09/%e5%90%8c%e4%b8%80%e4%b8%aarowid%e5%b0%b1%e4%b8%80%e5%ae%9a%e6%98%af%e5%90%8c%e4%b8%80%e6%9d%a1%e8%ae%b0%e5%bd%95%e5%90%97/ | 信春哥,系统稳,闭眼上线不回滚!

该日志由 dbdream 于2017年09月11日发表在 Oracle, oracle 11g 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: 同一个rowid就一定是同一条记录吗 | 信春哥,系统稳,闭眼上线不回滚!
关键字: ,

同一个rowid就一定是同一条记录吗:等您坐沙发呢!

发表评论

快捷键:Ctrl+Enter