事务隔离
在 Oracle 中,如果一个用户的事务没有提交,那么 只有该用户可以看到事务中的结果,而 其他用户无法看到,即使他们查询的是相同的数据表。这种行为是由 Oracle 实现的 事务隔离级别 和 读一致性 原则决定的。具体来说,Oracle 使用了 多版本并发控制(MVCC,Multi-Version Concurrency Control) 和 事务隔离 的机制来保证这一点。
1. 事务隔离级别和可见性
Oracle 数据库支持 ACID 属性(原子性、一致性、隔离性、持久性)中的 隔离性,通过不同的事务隔离级别来管理事务之间的并发行为。在默认情况下,Oracle 使用的是 READ COMMITTED 隔离级别。
READ COMMITTED(已提交读) 隔离级别
在 READ COMMITTED 隔离级别下:
- 已提交事务的修改才对其他事务可见。
- 未提交事务的修改 对其他事务 不可见,即便其他用户查询相同的表或数据。
解释原因:
Oracle 保证在 已提交读 隔离级别下,一个事务只能看到其他已提交事务所做的修改,而看不到未提交事务的修改。即使其他事务在同一时刻对同一数据进行了修改,只要这些事务没有提交,其他事务的修改对它们来说是不可见的。
2. 为什么未提交事务的结果只能被自己看到?
2.1 多版本并发控制(MVCC)
Oracle 使用 多版本并发控制(MVCC)来提供读一致性。对于一个正在执行的事务,Oracle 会为其查询提供一个快照,即查询时数据的“历史版本”。这个快照是事务开始时的数据库状态,所有后续的查询都会基于这个快照,确保事务中的查询不会看到其他事务尚未提交的数据。
具体来说:
- 当事务查询数据时,Oracle 会提供一个数据的一致视图,该视图只包含在当前事务开始之前已提交的修改。
- 如果某个事务修改了数据,但没有提交,其他事务无法看到这些修改。只有当事务提交时,这些修改才会对其他事务可见。
2.2 事务隔离与并发控制
Oracle 确保不同事务之间的隔离性,以避免“脏读”(dirty reads)、“不可重复读”(non-repeatable reads)和“幻读”(phantom reads)等问题:
- 脏读:指的是一个事务读取了另一个事务未提交的数据,这些数据如果未提交,则可能会被回滚,导致第一个事务读取到不真实的内容。
- 不可重复读:指的是一个事务在两次读取同一数据时,另一个事务在这两次读取之间修改了该数据,导致数据内容不一致。
- 幻读:指的是一个事务在读取数据时,另一个事务插入了新数据,导致查询结果集发生变化。
在 READ COMMITTED 隔离级别下,脏读 是被避免的,但 不可重复读 是可能的。而在 未提交事务 的情况下,Oracle 会完全隔离该事务的修改,避免其他事务看到未提交的更改。
MVCC
多版本并发控制(MVCC,Multi-Version Concurrency Control) 是一种数据库并发控制方法,它允许多个事务并发执行而不会互相干扰,同时确保数据库的一致性和隔离性。Oracle 数据库广泛使用 MVCC 来保证事务的隔离性,特别是在读一致性和避免脏读方面。
在 Oracle 中,MVCC 通过为每个数据行维护多个版本(或快照)来实现事务隔离。每个事务看到的数据库内容是事务开始时的一个一致性快照,**避免了“脏读”和“不可重复读”**的问题,并且支持高并发的数据访问。
1. MVCC 基本概念
MVCC 的核心思想是:每个事务对数据的修改都会生成一个新的数据版本,而其他事务则读取该数据时会看到一个历史版本(即事务开始时的数据快照)。这种方式允许多个事务同时操作同一数据,但彼此之间不会互相干扰。
关键要点:
- 数据版本:每次数据修改(如插入、更新)都会产生数据的新版本,每个版本会关联一个 事务ID,以标识该版本由哪个事务创建。
- 一致性快照:每个事务从开始时起都有一个数据库的“快照”,事务只能看到这个快照中的数据,而看不到其他事务未提交的更改。
- 撤销和重做(UNDO & REDO):事务修改的数据首先保存在内存中的 缓冲区 和 UNDO 表空间 中,直到事务提交或回滚。
2. MVCC 的工作原理
MVCC 在 Oracle 中的实现方式基于 事务标识符(TX ID) 和 时间戳 来维护数据版本,每次对数据进行修改时,数据库会生成一个新的版本,并且通过一些策略来确定不同事务对数据的可见性。
2.1 事务的版本控制
当事务对数据行进行操作(如 UPDATE
或 INSERT
)时,Oracle 会创建一个新的数据版本并将其存储在数据块(data block)中。在每个数据版本中,都会记录相关的 事务ID 和 时间戳 信息,标识该版本是由哪个事务创建的。
- 事务开始时的快照:当一个事务开始时,Oracle 会给它一个事务ID(TX ID)。该事务会看到它开始时数据库的快照,也就是说,它只能看到在该事务启动之前已经提交的数据。这确保了事务之间的隔离性。
- 数据行的版本管理:每当一个事务对某行数据进行修改时,会创建一个新的数据版本,同时会记录该数据版本的事务ID和其他元数据。这些新的数据版本存储在数据块中,并且与其他事务正在进行的操作隔离开来。
2.2 数据版本的维护
每个数据行都会与一个 撤销(UNDO)记录 和一个 数据版本标识(如事务ID)相关联。这样,Oracle 就能够维护多版本的数据。
- 数据行的当前版本:一个数据行在数据库中的每次更新都会产生一个新的版本。每个版本的存在都被标记上了事务ID,并且会有不同的 可见性范围,即该数据版本对哪些事务可见。
- 撤销(UNDO)信息:如果某个事务更新了数据并且未提交,那么这些更新会保存在 UNDO 表空间 中,以便在事务回滚时撤销修改。其他事务在查询时,不会看到这些未提交的修改。
2.3 事务隔离与数据可见性
MVCC 的关键在于 数据可见性,即哪些事务能够看到哪些数据版本。Oracle 的 MVCC 通过以下规则来决定数据行的可见性:
事务开始时的快照:每个事务在启动时都获得一个一致性快照,即数据库的当前状态,所有对该事务的查询将基于这个快照进行。这个快照反映了事务开始时数据库的状态,事务无法看到其他事务未提交的修改。
读取一致性:Oracle 确保事务始终读取到一致性的数据。例如,如果事务 A 在执行查询时,事务 B 对某行数据进行了更新,事务 A 不会看到事务 B 的修改,直到事务 B 提交并提交数据修改到数据文件中。换句话说,事务 A 只会读取到其启动时可见的数据版本,而不能看到其他事务的未提交更改。
数据版本的过期和清理:随着事务的进行,旧版本的记录会被标记为过期,不再可见,并且这些过期的数据会在合适的时候被清理(例如,垃圾回收),释放存储空间。
2.4 如何确保隔离性
MVCC 的隔离性通过以下机制来实现:
事务的快照隔离:当一个事务开始时,Oracle 会为其创建一个数据快照,确保事务内的查询只看到一致的旧版本数据,而不受其他并发事务影响。
撤销(UNDO)数据:当事务对数据进行修改时,Oracle 会先将修改前的原始数据保存在 UNDO 表空间 中,如果事务回滚,需要将这些修改撤销回去。如果事务没有提交,则其他事务无法看到这些修改。
提交和可见性:只有当事务提交时,它对数据所做的修改才会被 其他事务 可见。此时,修改后的数据版本会被持久化到数据文件,并且 REDO 日志 也会记录下这些修改,以便于数据库恢复。
3. 举例:MVCC 的实际操作
假设有两个事务,事务 A 和事务 B,它们操作同一行数据。
3.1 事务 A 执行:
- 事务 A 启动,并读取某个数据行(假设是
empno = 100
的员工记录)。Oracle 会为事务 A 提供该数据的快照,即事务 A 开始时的版本。假设此时该行数据的salary
为 3000。 - 事务 A 对
salary
字段执行UPDATE
,将其修改为 4000。Oracle 会创建一个新的数据版本,并将该版本与事务 A 的事务 ID 关联。数据的salary
字段现在在内存中是 4000,但原数据文件中的salary
仍然是 3000(未提交)。
3.2 事务 B 执行:
- 事务 B 启动,并且在事务 A 提交之前,事务 B 也查询
empno = 100
的记录。由于事务 B 开始时的快照是事务 B 启动时的数据库状态,它将看到 事务 A 修改之前的salary
值,即 3000,因为事务 B 不会看到事务 A 未提交的修改。
3.3 事务 A 提交:
- 当事务 A 提交时,它修改的数据(
salary = 4000
)会被写入原始数据文件。此时,事务 A 的修改对其他事务可见。 - 事务 B 如果再次查询该记录,将看到
salary = 4000
,这是因为事务 A 提交后,修改变得对所有事务可见。
4. MVCC 的优点
MVCC 的使用带来以下优点:
- 高并发性:MVCC 允许多个事务并发执行,因为它们对数据的修改不会立即影响其他事务。每个事务都能看到它自己开始时的数据快照,避免了资源争用和锁竞争。
- 读一致性:MVCC 可以为每个事务提供一致的视图,避免脏读、不可重复读等问题。每个事务的查询操作都基于其开始时的快照,确保了数据的一致性。
- 回滚操作:当事务回滚时,Oracle 使用 UNDO 数据 恢复原始数据,而不需要直接修改已经提交的数据。
5. MVCC 的缺点
虽然 MVCC 提供了很多优势,但它也有一些缺点:
- 空间开销:每次修改都会生成新的数据版本,需要额外的存储空间来维护数据的多个版本和撤销记录,尤其是当事务非常频繁时。
- 版本清理:随着数据版本的增多,Oracle 需要定期清理过期的版本,释放存储空间。这一过程有时可能会导致性能问题,特别是在高并发和长时间运行的事务中。
Undo 段 在 Oracle 数据库中的三个主要用途:
- 事务回滚(Transaction Rollback):
- 当事务修改了表中的数据时,Oracle 会将修改前的数据(称为 Undo 数据)保存到 Undo >段中。如果事务回滚,Oracle 会使用 Undo 数据将数据恢复到修改前的状态。
- 事务恢复(Transaction Recovery):
- 如果数据库实例在事务执行过程中发生故障,当数据库重新启动时,Oracle 会使用 Undo 段中的数据来撤销未提交的更改,确保数据库恢复到一致的状态。这一过程依赖于 Undo 数据和重做日志的配合。
- 读一致性(Read Consistency):
- 在事务进行时,其他用户不能看到未提交的修改。同时,一个查询语句执行时,不能看到该语句开始后提交的任何更改。Undo 段中的旧数据确保了查询结果的一致性,避免了脏读现象的发生。
undo表空间的增加、删除、切换
1. 增加 Undo 表空间(Add Undo Tablespace)
在 Oracle 数据库中,如果现有的 Undo 表空间空间不足,或者为了提高性能,需要增加新的 Undo 表空间。增加 Undo 表空间的操作通常有两种方式:
- 创建新的 Undo 表空间:这会为数据库创建一个新的 Undo 表空间,新的事务会开始使用这个表空间。
- 扩展现有的 Undo 表空间:如果现有的 Undo 表空间没有足够的空间,可以通过添加数据文件来扩展它。
(1) 创建新的 Undo 表空间
创建新的 Undo 表空间时,需要指定表空间的名称和数据文件的位置及大小。比如:
-- 创建新的 Undo 表空间
CREATE UNDO TABLESPACE undotbs2
DATAFILE '/u01/app/oracle/oradata/ORCL/undotbs02.dbf'
SIZE 200M
AUTOEXTEND ON NEXT 20M MAXSIZE UNLIMITED;
- UNDO TABLESPACE undotbs2:指定新的 Undo 表空间的名称。
- DATAFILE ‘/u01/app/oracle/oradata/ORCL/undotbs02.dbf’:指定数据文件的路径和名称。
- SIZE 200M:初始大小为 200MB。
- AUTOEXTEND ON NEXT 20M MAXSIZE UNLIMITED:允许数据文件自动扩展,每次扩展 20MB,最大大小无限制。
(2) 扩展现有的 Undo 表空间
如果现有的 Undo 表空间已经用完,可以通过添加数据文件来扩展它。使用以下命令:
-- 向现有的 Undo 表空间添加数据文件
ALTER DATABASE DATAFILE '/u01/app/oracle/oradata/ORCL/undotbs01.dbf'
RESIZE 500M;
或者,添加新的数据文件:
-- 向现有的 Undo 表空间添加新的数据文件
ALTER TABLESPACE undotbs1
ADD DATAFILE '/u01/app/oracle/oradata/ORCL/undotbs03.dbf' SIZE 200M;
- RESIZE 500M:将现有的 Undo 数据文件大小调整为 500MB。
- ADD DATAFILE:向现有的 Undo 表空间添加新的数据文件。
2. 删除 Undo 表空间(Drop Undo Tablespace)
删除 Undo 表空间时,首先需要确保没有活动的事务使用这个表空间。删除 Undo 表空间通常需要以下步骤:
- 切换到另一个 Undo 表空间:确保所有事务使用新的 Undo 表空间。
- 删除旧的 Undo 表空间。
(1) 切换到新的 Undo 表空间
在删除 Undo 表空间之前,必须先将数据库的 Undo 表空间切换到另一个表空间。
-- 切换到新的 Undo 表空间
ALTER SYSTEM SET UNDO_TABLESPACE = undotbs2; # 将 Undo 表空间切换到 `undotbs2`。
(2) 删除 Undo 表空间
一旦切换到新的 Undo 表空间,您可以删除旧的 Undo 表空间。注意,如果 Undo 表空间中有未使用的文件,可以直接删除;否则,可能需要手动删除其中的文件。
-- 删除 Undo 表空间
DROP TABLESPACE undotbs1 INCLUDING CONTENTS AND DATAFILES;
- INCLUDING CONTENTS AND DATAFILES:删除 Undo 表空间及其内容和数据文件。
DROP TABLESPACE undotbs1; # 只是想删除表空间本身,而不删除数据文件
3. 切换 Undo 表空间(Switch Undo Tablespace)
切换 Undo 表空间是指改变数据库使用的 Undo 表空间,这通常在系统维护、扩展或清理时发生。通过设置 UNDO_TABLESPACE
参数来切换。
(1) 查看当前的 Undo 表空间
-- 查看当前的 Undo 表空间
SHOW PARAMETER UNDO_TABLESPACE;
或者:
-- 查看当前 Undo 表空间的名称
SELECT * FROM v$parameter WHERE name = 'undo_tablespace';
(2) 切换 Undo 表空间
如前所述,切换 Undo 表空间需要使用 ALTER SYSTEM
命令:
-- 切换到新的 Undo 表空间
ALTER SYSTEM SET UNDO_TABLESPACE = undotbs2;
这样,所有新的事务和操作将开始使用新的 Undo 表空间 undotbs2
。