变与不变: Undo构造一致性读的例外情况

简介:

其中介绍了Oracle如何使用UNDO来实现多版本一致性读,使用了OPEN CURSOR的方式非常巧妙地在很少量数据的情况下构造出可重现的案例。不过这个案例存在一点小的瑕疵,因为如果一不小心,很可能会导致结果与预期不符,这是因为这里有一个例外存在。

我们先来模拟一下UNDO构造一致性读的情况,对于Oracle而言,默认的隔离级别是READ COMMIT,也就是说一个会话只能看到其他会话已经提交的修改,未提交的修改或者在当前会话查询发起之后提交的修改都是不可见的。

再介绍一下OPEN CURSOR,Oracle中当一个游标被打开,其结果集就已经确定了,也就是说这个游标会根据OPEN CURSOR这个时间点对应的SCN来构造一致性查询。但是OPEN CURSOR时,对应的SQL并不会被执行,在后续FETCH的时候(对于SQLPLUS而言PRINT命令会触发FETCH),SQL才真正被执行。使用这种办法可以模拟一个大的查询,OPEN CURSOR相当于大的查询的开始时间,其早于其他会话的修改提交时间,而FETCH的时间相当于大查询读取到这条记录的时间,而该时间晚于其他会话提交的时间:

SQL> SET SQLP 'SQL1> '

SQL1> CREATE TABLE T_UNDO (ID NUMBER, NAME VARCHAR2(30));


Table created.

SQL1> INSERT INTO T_UNDO SELECT ROWNUM, OBJECT_NAME FROM DBA_OBJECTS;

96920 rows created.

SQL1> COMMIT;

Commit complete.

SQL1> CREATE INDEX IND_UNDO_ID ON T_UNDO(ID);

Index created.

SQL1> SELECT NAME FROM T_UNDO WHERE ID = 1119;

NAME

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

I_EXTERNAL_LOCATION1$

SQL1> VAR C REFCURSOR

SQL1> EXEC OPEN :C FOR SELECT NAME FROM T_UNDO WHERE ID = 1119;

PL/SQL procedure successfully completed.

在第一个会话已经构造了一个查询,下面在会话2对这条ID为1119的记录进行修改并提交:

SQL> SET SQLP 'SQL2> '

SQL2> UPDATE T_UNDO SET NAME = 'UPDATED' WHERE ID = 1119;

1 row updated.

SQL2> COMMIT;

Commit complete.

在会话3上执行查询,这时会看到会话2修改提交后的结果:

SQL> SET SQLP 'SQL3> '

SQL3> SELECT NAME FROM T_UNDO WHERE ID = 1119;

NAME

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

UPDATED

回到会话1,对CURSOR变量执行PRINT,检查得到的结果:

SQL1> PRINT :C

NAME

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

I_EXTERNAL_LOCATION1$

到目前为止,所有都是预期之内的结果,Oracle会利用UNDO来存储UPDATE的前镜像,当查询发现需要访问的数据块SCN大于会话发起的SCN,而需要通过UNDO中存储的前镜像来构造一致性读,找到会话需要读取的修改前的数据。

那么例外来自哪里呢,在这个例子中,我们给ID列上创建了一个索引,如果这不是一个普通的索引,而是一个主键,那么效果如何呢:

SQL1> DROP INDEX IND_UNDO_ID;

Index dropped.

SQL1> ALTER TABLE T_UNDO ADD PRIMARY KEY (ID);

Table altered.

SQL1> SELECT NAME FROM T_UNDO WHERE ID = 1118;

NAME

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

EXTERNAL_LOCATION$

SQL1> EXEC OPEN :C FOR SELECT NAME FROM T_UNDO WHERE ID = 1118;

PL/SQL procedure successfully completed.

会话2修改ID为1118的记录:

SQL2> UPDATE T_UNDO SET NAME = 'UPDATED WITH PK' WHERE ID = 1118;

1 row updated.

SQL2> COMMIT;

Commit complete.

会话3检查确认修改结果:

SQL3> SELECT NAME FROM T_UNDO WHERE ID = 1118;

NAME

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

UPDATED WITH PK

再次回到会话1,PRINT游标变量:

SQL1> PRINT :C

NAME

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

UPDATED WITH PK

可以看到例外产生了,一致性读的结果被破坏了,居然可以查询到发生在游标打开之后提交的修改。

导致这个例外的原因来自于一个隐含函数_row_cr:

04aeb6986ef36504666090713fc6865222342c2b

Oracle11g以后,这个隐含参数默认值修改为TRUE,这使得Oracle对于基于主键的访问不再采用默认的一致性读方案。当然Oracle做出这种修改的目的是为了提高性能,而且仅对于单行访问生效,而大部分情况下单行访问的效率非常高,因此对于一致性破坏的影响并不明显。到18C为止,该参数仍然为TRUE。

如果关闭该参数:

SQL1> ALTER SYSTEM SET "_row_cr" = FALSE;

System altered.

SQL1> SELECT NAME FROM T_UNDO WHERE ID = 1117;

NAME

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

I_EXTERNAL_TAB1$

SQL1> EXEC OPEN :C FOR SELECT NAME FROM T_UNDO WHERE ID = 1117;

PL/SQL procedure successfully completed.

会话2进行修改:

SQL2> UPDATE T_UNDO SET NAME = 'UPDATED NO ROW CR' WHERE ID = 1117;

1 row updated.

SQL2> COMMIT;

Commit complete.

检查结果:

SQL3> SELECT NAME FROM T_UNDO WHERE ID = 1117;

NAME

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

UPDATED NO ROW CR

回到会话1检查结果:

SQL1> PRINT :C

NAME

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

I_EXTERNAL_TAB1$

Oracle恢复默认的读一致性隔离级别。

虽然Oracle认为这种优化只是针对主键或唯一索引等行级访问生效,造成数据一致性破坏的可能性很小,但是建议对于一致性要求较高的行业尤其是金融相关行业还是将该特性关闭,避免因此造成的一致性问题。


原文发布时间为:2018-11-20

本文作者:xxx

本文来自云栖社区合作伙伴“数据和云”,了解相关信息可以关注“数据和云”。

相关文章
发布宝贝提示“亲,您未通过食品资质备案所以无法新发商品”如何解决
亲,您未通过食品资质备案所以无法新发商品!根据《中华人民共和国食品安全法》要求,经营该类目下商品(食用农产品除外)需提供食品经营或食品生产资质,<a href='https://t.tb.cn/5CSyQjdLA5q33HBQElFNGd' target='_blank'>点击查看资质要求学习链接</a>,<a href='https://scportal.taobao.com/quali/portal.htm?source=taobao' target='_blank'>点击立即上传资质</a>,经营不同类型的食品,提交资质时,请您注意“经营范围”的选择。
webpack 打包去掉控制台的console.log()
webpack 打包去掉控制台的console.log()
1312 0
webpack 打包去掉控制台的console.log()
|
8月前
|
API
微店商品详情API接口调用指南
本指南详解微店商品详情数据获取接口weidian.item.get,涵盖合规调用方式、核心参数(如item_id、sign)及返回字段(标题、价格、库存等),适用于电商分析与代购系统,确保数据准确稳定。
|
8月前
|
人工智能 弹性计算 API
2026阿里云人工智能PAI免费试用:PAI-EAS模型在线服务A10/V100等500元1个月
阿里云PAI推出模型在线服务PAI-EAS,支持Qwen3、Llama等主流模型一键部署为高可用API,弹性伸缩、GPU加速,新用户享500元代金券,免费试用A10/V100等GPU实例,助力低成本高效推理。
|
Linux
68Linux - 解决CentOS启动卡住了,提示starting auditd: [failed]
68Linux - 解决CentOS启动卡住了,提示starting auditd: [failed]
557 0
|
机器学习/深度学习 自然语言处理 测试技术
Qwen3技术报告首次全公开!“混合推理模型”是这样炼成的
近日,通义千问Qwen3系列模型已开源,其技术报告也正式发布。Qwen3系列包含密集模型和混合专家(MoE)模型,参数规模从0.6B到235B不等。该模型引入了“思考模式”与“非思考模式”的动态切换机制,并采用思考预算机制优化推理性能。Qwen3支持119种语言及方言,较前代显著提升多语言能力,在多个基准测试中表现领先。此外,通过强到弱蒸馏技术,轻量级模型性能优异,且计算资源需求更低。所有Qwen3模型均采用Apache 2.0协议开源,便于社区开发与应用。
8345 30
|
移动开发 小程序
仿青藤之恋社交交友软件系统源码 即时通讯 聊天 微信小程序 App H5三端通用
仿青藤之恋社交交友软件系统源码 即时通讯 聊天 微信小程序 App H5三端通用
1591 3
|
消息中间件 存储 Kafka
2024最全Kafka集群方案汇总
Apache Kafka 是一个高吞吐量、可扩展、可靠的分布式消息系统,广泛应用于数据驱动的应用场景。Kafka 支持集群架构,具备高可用性和容错性。其核心组件包括 Broker(服务器实例)、Topic(消息分类)、Partition(有序消息序列)、Producer(消息发布者)和 Consumer(消息消费者)。每个分区有 Leader 和 Follower,确保数据冗余和高可用。Kafka 2.8+ 引入了不依赖 Zookeeper 的 KRaft 协议,进一步简化了集群管理。常见的集群部署方案包括单节点和多节点集群,后者适用于生产环境以确保高可用性。
1144 0
|
小程序 JavaScript 前端开发
从零玩转系列之小程序微信支付UniApp实战基础项目搭建
从零玩转系列之小程序微信支付UniApp实战基础项目搭建
598 0
从零玩转系列之小程序微信支付UniApp实战基础项目搭建

热门文章

最新文章