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

杨廷琨 云和恩墨高级咨询顾问, 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

作者:杨廷琨。

转载:意向文章下方留言。

原文发布于微信公众号 - 数据和云(OraNews)

原文发表时间:2018-07-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏腾讯云Elasticsearch Service

mysql数据实时同步到Elasticsearch

业务需要把mysql的数据实时同步到ES,实现低延迟的检索到ES中的数据或者进行其它数据分析处理。本文给出以同步mysql binlog的方式实时同步数据到ES...

4.5K7
来自专栏lgp20151222

mysql explain 的extra中using index ,using where,using index condition,using index & using where理解

using where:查找使用了索引,不需要回表去查询所需的数据,查询结果是索引的一部分

1351
来自专栏性能与架构

MySQL 8.0 将结束 MyISAM 引擎

MyISAM 存储引擎已经有了20年的历史,在1995年时,MyISAM 是 MySQL 唯一的存储引擎,服务了20多年,即将退居二线 MySQL 5.7 中...

3386
来自专栏IT开发技术与工作效率

MySQL数据库优化总结《高性能MySQL》指导其他指导

3424
来自专栏ImportSource

锁系列-Mysql中的锁

在计算机科学中,锁是在执行多线程时用于强行限制资源访问的同步机制,即用于在并发控制中保证对互斥要求的满足。 目录: 1、行级锁、表级锁、页级锁 2、共享锁和排它...

37415
来自专栏跟着阿笨一起玩NET

SQL语句使用总结(一)

1>. FROM 2>. WHERE 3>. GROUP BY 4>. HAVING 5>. SELECT 6>. ORDER BY

611
来自专栏用户2442861的专栏

MySQL详解--锁

锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供许多用户共享的资源。如...

963
来自专栏Clive的技术分享

Mysql锁相关锁的分类锁的适用场景MyISAM表锁MyISAM写阻塞读的例子MyISAM读阻塞写例子MyISAM并发插入MyISAM读写并发MyISAM锁调度调节MyISAM锁调度行为解决读写冲突的

锁类型/引擎 行锁 表锁 页锁 MyISAM 有 InnoDB 有 有 BDB(被InnoDB取代) 有 有 锁的分类 表...

4595
来自专栏杨建荣的学习笔记

通过shell脚本模拟MySQL自增列的不一致问题

MySQL的自增列问题其实很有意思,在重启数据库之后,会按照max(id)+1的方式来计算,这样一个看起来有些别扭的实现方式在早期版本就饱受诟病,在MyS...

3494
来自专栏PHP在线

Mysql存储引擎中InnoDB与Myisam的区别

1. 事务处理innodb 支持事务功能,myisam 不支持。 Myisam 的执行速度更快,性能更好。 2. select ,update ,insert...

3175

扫码关注云+社区

领取腾讯云代金券