oracle中ddl不能回滚的问题【转载】

本文探讨了Oracle数据库中DDL语句不可回滚的原因及其对并发性的影响,对比了其他数据库的做法。
[size=medium]原文地址:
http://www.2cto.com/database/201209/155881.html
作者:foreverfeng
文章解释不错,转载一下,作为个人资料学习库。晓得DDL是oracle为了增强并发性和减少锁的产生,就OK了。[/size]


在ITPUB上看到有人提出了这个问题。在Sqlserver或一些其他的数据库中,DDL语句也是可以回滚的,那么Oracle为什么不能回滚DDL语句呢。

这个问题来自:http://www.itpub.net/thread-1300088-1-5.html

要说明这个问题,首先需要说明什么是DDL语句。DDL语句是数据定义语句,包括各种数据对象的创建、修改和删除,以及授权等操作。

在Oracle中DDL语句将转化为修改数据字典表的DML语句。一个简单的修改表的DDL语句,会导致Oracle在后台通过递归SQL语句进行大量的查询和修改的操作。

如果有兴趣,可以通过SQL_TRACE根据一下DDL语句,检查一下Oracle后台实际执行了哪些操作。

在Oracle中,Oracle执行DDL前会发出一个COMMIT语句,然后执行DDL操作,最后再发出一个COMMIT操作。

前面提到了对于Oracle而言,DDL实际上是数据字典表的一系列的修改,也就是数据字典表的DML操作,那么理论上讲Oracle是完全有能力实现DDL语句的回滚的,那么Oracle为什么设计成现在的工作方式。要知道Oracle以灵活和强大的可定制性著称,但是Oracle没有给用户任何回滚DDL的可能性,显示是存在着十分充分的理由。

首先分析一下Oracle为什么要在DDL语句之前和之后各执行一次COMMIT,其实道理很简单,Oracle是为了将用户的读写操作和数据字典的修改隔离开,用户数据的读写不应该和数据字典的操作放在同一个事务中。
www.2cto.com
为了说明Oracle为什么不回滚DDL语句,下面假设Oracle可以回滚DDL语句,看看这会给Oracle数据库带来什么影响。

从现在开始,假设DDL并不会自动提交,而是事务中的一部分。

那么DDL就要满足READ COMMIT隔离机制,也就是说,用户执行的DDL语句在提交前,其他用户是无法看到的。比如A用户执行CREATE TABLE T的语句,然后对T执行了一些DML。而这时其他会话是无法看到T表的。

那么考虑这样的情况,存在表T,包含两个列,一个ID列,一个CREATED列。

A会话执行了ALTER TABLE T MODIFY CREATED DEFAULT SYSDATE NOT NULL,然后对T表进行了一些插入,但是没有提交。

这时B会话尝试插入T表,如果DDL语句不是事务的一部分,那么B的插入和A会话的插入之间没有冲突,但是现在情况不同,由于A执行了T表的修改,为CREATED列增加了默认值并设置为NOT NULL,而且这个修改B会话当前是看不到的,因为A并没有提交修改。这时如果B会话的插入没有提供CREATED列的值,则插入操作将被锁定。

对于B而言,表结构中CREATED列仍然是可空的,因此允许插入CREATED列为空的记录,但是由于A已经设置T的CREATED列非空,且包含默认值,因此B的插入必须被锁定,否则如果A和B全部提交,A会话会发现即使执行了DDL语句,T表中仍然存在CREATED为空的记录。Oracle为了实现DDL可以回滚的功能,且实现多版本读一致性,那么就必须在DDL发生后,将修改的表锁定,避免其他会话的访问造成不一致。这会导致Oracle中出现锁升级的情况,并且严重的影响Oracle的并发性,而且会大大增加死锁产生的几率。

也许有人奇怪SQLSERVER或一些其他的数据库为什么可以实现DDL语句的回滚。事实上,前面提到了Oracle也是有能力实现DDL回滚的,只是这会极大的影响Oracle的并发性。要知道,Oracle的锁机制和多版本读一致性使得Oracle的并发性在所有数据库产品中首屈一指。显然为了实现DDL的回滚而损失最值得称道的并发性,Oracle认为得不偿失。

其他数据库之所以可以实现,是因为这些数据库的锁机制本身就存在一定缺陷,比如大量的锁会占用系统的资源、读写操作互相阻塞、行级锁可能自动升级为表级锁。由于已经存在这些问题,所以实现DDL的回滚并不会在很大程度上使得并发性恶化,因为即使DDL不将行锁升级为表锁,可能其他的因素也会导致这种情况的发生。
<think> 嗯,用户这次的问题很具体,是关于Oracle数据库DELETE操作如何绕过缓存直接进行物理删除并实现回滚的机制。用户之前提到了三个引用片段,但看起来真正需要的是底层事务处理原理。 用户可能是个Oracle DBA或者开发人员,遇到了性能调优或数据一致性的实际问题。ta特意强调“不走缓存”,说明可能遇到过缓存带来的性能瓶颈或延迟问题。引用中提到的外键约束级联删除也可能暗示用户在处理复杂数据关系时遇到了事务控制需求。 回看对话历史,之前讨论过Oracle基础操作和事务处理,但没深入到底层机制。这次需要重点解释REDO日志和UNDO表空间这两个核心组件。用户提到的“不走缓存”其实对应的是Oracle的buffer cache机制,而直接路径操作(Direct-Path)确实是解决方案。 技术上要分三部分解答: 首先是常规DELETE的流程:数据先读入buffer cache修改,生成UNDO记录和REDO日志。这样虽然安全但效率低,特别是大表删除时。用户显然想避开这个瓶颈。 然后是替代方案:TRUNCATE和DROP确实能绕过buffer cache,但牺牲了事务回滚能力,不符合用户“需要回滚”的要求。这里要强调DDL和DML的本质区别。 最后是折中方案——直接路径操作。Oracle的/*+ APPEND */提示符不仅能用于INSERT,配合DELETE也有类似效果,但需要说明具体语法差异。用户可能不知道这个冷门技巧。 原理部分要简明:UNDO表空间存储前镜像实现回滚,REDO日志保证持久性。重点在于解释为什么直接路径操作不产生UNDO,而是依赖空间回收机制。用户可能担心这种方法的数据安全性,需要强调它仍然满足ACID的持久性标准。 实践建议部分,用户真正需要的是可操作的SQL范例和风险提示。比如回收空间操作会锁表,生产环境要谨慎。附带生成的相关问题应该覆盖性能优化和灾难恢复场景,这可能是用户后续会追问的方向。 </think> 在Oracle数据库中实现DELETE操作绕过缓存(Buffer Cache)并确保可回滚的核心机制涉及以下技术要点: --- ### 一、常规DELETE流程与缓存问题 1. **默认行为** 执行`DELETE`语句时,Oracle会: - 将数据块从磁盘读入Buffer Cache(除非已在缓存中) - 在Buffer Cache中修改数据(标记为删除) - 生成UNDO记录(存储前镜像,用于回滚)和REDO日志(保证持久性) - **性能瓶颈**:大表删除时,频繁的缓存读写和UNDO生成可能导致高I/O负载。 --- ### 二、绕过缓存的方法:直接路径操作 **使用 `TRUNCATE` 或 `DROP` + 重建表** - **原理**: 这些DDL操作直接修改数据文件,**跳过Buffer Cache和UNDO生成**(仅生成少量REDO日志)。 ```sql TRUNCATE TABLE employees; -- 瞬间清空表,不可回滚 ``` - **局限性**: - ❌ **无法回滚**:DDL操作隐式提交事务,违反可回滚需求。 - ❌ **外键约束冲突**:若存在外键引用(如引用[3]的约束),需先禁用约束。 --- ### 三、替代方案:基于UNDO的优化删除 #### 方法1:分批删除 + 提交回滚段释放 ```sql BEGIN LOOP DELETE FROM big_table WHERE rownum <= 10000; -- 分批删除 EXIT WHEN SQL%ROWCOUNT = 0; COMMIT; -- 释放UNDO空间 END LOOP; END; ``` **优势**: - 避免UNDO表空间爆满(通过多次提交) - **仍依赖Buffer Cache**,但减少单次事务负载 --- #### 方法2:直接路径插入 + 表切换(需停机) ```sql CREATE TABLE new_table NOLOGGING AS SELECT * FROM old_table WHERE status != 'DELETE'; -- 绕过缓存插入有效数据 ALTER TABLE old_table RENAME TO backup_table; ALTER TABLE new_table RENAME TO old_table; ``` **原理**: - `NOLOGGING` 减少REDO日志(但需备份后启用) - 数据直接写入数据文件,跳过Buffer Cache - **可回滚性**:通过重命名操作前的备份表实现 --- ### 四、关键原理:UNDO与REDO机制 1. **UNDO表空间** - 存储数据前镜像(引用[2]的"回滚"依赖此机制) - `DELETE`操作会生成UNDO记录,允许`ROLLBACK`恢复数据 - 直接路径操作(如`TRUNCATE`)不生成UNDO,故不可回滚 2. **REDO日志** - 记录所有变更(含UNDO),保证故障恢复 - 即使绕过Buffer Cache(如直接路径写入),仍需REDO确保持久性 --- ### 五、实践建议 | **场景** | **推荐方法** | **可回滚** | **绕过缓存** | |--------------------------|------------------------|------------|--------------| | 小型表删除 | 直接`DELETE` | ✓ | ✗ | | 大表删除(允许停机) | 表切换 + 直接路径插入 | 通过备份 | ✓ | | 大表删除(不允许停机) | 分批删除 + 多次提交 | ✓ | ✗ | | 无需回滚的快速清空 | `TRUNCATE` | ✗ | ✓ | > **注意**:外键约束可能阻断`TRUNCATE`(引用[3]),需先处理约束: > ```sql > ALTER TABLE child_table DISABLE CONSTRAINT fk_name; > TRUNCATE TABLE parent_table; > ALTER TABLE child_table ENABLE CONSTRAINT fk_name; > ``` --- ### 六、相关问题 1. 如何监控Oracle中UNDO表空间的使用压力? 2. 直接路径操作(`/*+ APPEND */`)在INSERT中的原理与DELETE有何异同? 3. 启用`FLASHBACK TABLE`功能对删除操作的回滚有何增强? 4. 外键约束的`ON DELETE CASCADE`(引用[3])如何影响大批量删除性能? [^1]: Oracle数据库基础操作指南 [^2]: 数据库操作结果返回机制 [^3]: 外键约束语法与级联行为
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值