深入解析:你听说过Oracle数据库的更新重启动吗?

来自:数据和云(微信号:OraNews),作者:杨廷琨
作者介绍:杨廷琨
云和恩墨高级咨询顾问, ITPUB Oracle 数据库管理版版主 ,人称 “杨长老”,十数年如一日坚持进行 Oracle 技术研究与写作,号称 “Oracle 的百科全书”。迄今已经在自己的博客上发表了超过 3000 篇技术文章。2010 年,与 Eygle 共同主编出版了《Oracle DBA 手记》一书,2007 年被 Oracle 公司授予 ACE 称号。

事件背景

最近看到一个比较有意思的例子,是和更新重启动有关。


更新重启动是 Oracle 中一个隐藏的知识点,当 Oracle 在读取数据时,会构造读一致性,当读取的内容发生了修改,Oracle 会通过 UNDO 信息来还原数据的前镜像,把数据还原到查询发生的时刻,通过构造一致性的结果来实现读取数据的一致性和隔离性。


Oracle 实现的读一致性也被称为多版本读一致性,每个会话都会构造自己的一致性查询版本。但是对于写操作而言,这种方式是不可能的,因为最终的数据只有一份,如要要修改数据,就只能修改唯一的这份数据,所以对读操作,访问的是一致性版本,而对于写操作,修改的永远是当前版本。


既然写操作只能修改当前版本,当写操作执行的过程中,发现要修改的记录发生了变化,破坏了更新发起时刻的一致性,这时就触发了更新重启动,也就是说更新操作会放弃之前的修改,然后重新发起一次新的更新操作。


案例一


ITPUB 上有一个有意思的案例,展示的就是更新重启动现象,链接如下:

http://www.itpub.net/forum.php?mod=viewthread&tid=2102897


这里来重现一下更新重启动,建立一张简单测试用表,在会话一插入初始数据并进行更新操作:

SQL> SET SQLP 'SQL1> '

SQL1> CREATE TABLE T_UPDATE (ID NUMBER);


Table created.

SQL1> INSERT INTO T_UPDATE SELECT ROWNUM FROM DUAL CONNECT BY LEVEL < 4;


3 rows created.

SQL1> COMMIT;


Commit complete.

SQL1> SELECT * FROM T_UPDATE;

        ID

----------

         1

         2

         3

SQL1> UPDATE T_UPDATE SET ID = 4 WHERE ID > 2;


1 row updated.


在会话 2 发起 UPDATE 语句,更新 ID 大于 0 的记录,这时 UPDATE 操作由于会话 1 更新了 ID 为 3 的记录且未提交,会处于挂起状态:

SQL> SET SQLP 'SQL2> ' 

SQL2> UPDATE T_UPDATE SET ID = ID + 0.1 WHERE ID > 0;


然后在会话 3 插入一条新的记录,并提交:

SQL> SET SQLP 'SQL3> '

SQL3> INSERT INTO T_UPDATE VALUES (0.1);


1 row created.

SQL3> COMMIT;


Commit complete.


这时回到会话1,进行提交:

SQL1> COMMIT;

Commit complete.


检查会话 2,发现更新重启动发生,UPDATE 操作更新了 4 条记录:

4 rows updated.

SQL2> SELECT * FROM T_UPDATE;

        ID

----------

       1.1

       2.1

       4.1

        .2


会话 2 中不仅更新了之前存在的三条记录,连更新语句发起之后插入并提交的第四条记录也一起进行了更新,这说明更新重启动发生在第四条记录插入之后。


论坛里问题的时序和结果如下,大家也可以参考这个范例,理解更新重启动的含义:



这个案例很好的展示了更新重启动现象,不过这个帖子并没有完,除了这个更新重启动的案例外,还展示了一个另外一个案例,操作和这个案例仅有很小的差别,但是结果却大不相同。

案例2

首先把数据恢复到初始状态,在会话 2 回滚之前的更新:

SQL2> ROLLBACK;     


Rollback complete.


在会话1,恢复初始的数据情况,然后开始更新数据

SQL1> DELETE T_UPDATE WHERE ID < 1;


1 row deleted.

SQL1> UPDATE T_UPDATE SET ID = 3 WHERE ID = 4;


1 row updated.

SQL1> COMMIT;


Commit complete.

SQL1> SELECT * FROM T_UPDATE;

        ID

----------

         1

         2

         3

SQL1> UPDATE T_UPDATE SET ID = 4 WHERE ID > 2;


1 row updated.


在会话 2,对表中所有的记录的 ID 执行 UPDATE 操作,和上一个案例的唯一差别是,这里没有使用 WHERE 语句:

SQL2> UPDATE T_UPDATE SET ID = ID + 0.1;


同样在会话3插入数据并提交:

SQL3> INSERT INTO T_UPDATE VALUES (0.1);


1 row created.

SQL3> COMMIT;


Commit complete.


回到会话1,进行提交:

SQL1> COMMIT;


Commit complete.


发现会话 2 仅更新了 3 条记录:

3 rows updated.


SQL2> SELECT * FROM T_UPDATE;

        ID

----------

       1.1

       2.1

       4.1

        .1


可以清晰的看到,最后一条插入的记录并未被更新,说明更新重启动并未被触发。

对比分析

为什么带有 WHERE 条件的更新操作触发了更新重启动,而不带 WHERE 条件的更新未触发重启动呢,我是这样理解的:当 UPDATE 操作包含了 WHERE 条件,那么这个查询的结果要满足 WHERE 定义的查询一致性,当更新发现一致性不满足的情况下,就会触发更新重启动。


而对于不包含 WHERE 条件或包含 WHERE 条件但是该条件与表查询无关的情况,这时 Oracle 的目标只是将表中所有的数据进行一次更新,并不需要考虑一致性的问题。因此,这个不加 WHERE 条件的 UPDATE,感觉其实现方式上类似这种通过 ORA_ROWSCN 限定来实现:

SQL2> ROLLBACK;


Rollback complete.


回到会话 1 恢复数据,并检查 ORA_ROWSCN 的值:

SQL1> DELETE T_UPDATE WHERE ID < 1;


1 row deleted.

SQL1> UPDATE T_UPDATE SET ID = 3 WHERE ID = 4;


1 row updated.

SQL1> COMMIT;


Commit complete.

SQL1> SELECT ID, ORA_ROWSCN FROM T_UPDATE;

        ID ORA_ROWSCN

---------- ----------

         1  231512964

         2  231512964

         3  231512964

SQL1> UPDATE T_UPDATE SET ID = 4 WHERE ID > 2;


1 row updated.

 

在会话 2,发起包含制定 ORA_ROWSCN 限制条件的更新:

SQL2> UPDATE T_UPDATE SET ID = ID + 0.1 WHERE ORA_ROWSCN = 231512964;


会话 3 插入并提交:

SQL3> INSERT INTO T_UPDATE VALUES (0.1);


1 row created.

SQL3> COMMIT;


Commit complete.


回到会话 1,进行提交:

SQL1> COMMIT;


Commit complete.


检查会话2:

3 rows updated.

SQL2> SELECT * FROM T_UPDATE;


        ID

----------

       1.1

       2.1

       4.1

        .1


采用 ORA_ROWSCN 的效果与直接 UPDATE 不带任何 WHERE 条件是一样的,说明更新只关注 UPDATE 语句发出时刻的数据,不再考虑整体更新结果的一致性问题。


关于更新重启动的更详细的描述,建议参考ASKTOM上的回复:

https://asktom.oracle.com/pls/apex/asktom.search?tag=write-consistency

推荐↓↓↓
数据库开发
上一篇:Oracle 有关10053事件,你知道这两个知识点么? 下一篇:Redis集群搭建