Skip to content

Oracle 事务与锁机制

事务与锁机制是 Oracle 数据库并发控制的核心,理解事务和锁的工作原理对于 DBA 进行性能调优、故障诊断和日常维护至关重要。

事务基础

事务的定义

事务是一个或多个 SQL 语句的逻辑单元,这些语句要么全部执行成功,要么全部失败。事务是数据库并发控制的基本单位。

ACID 特性

Oracle 事务具有 ACID 特性:

  • 原子性(Atomicity):事务是一个不可分割的工作单元,要么全部执行成功,要么全部失败回滚
  • 一致性(Consistency):事务执行前后,数据库从一个一致状态转换到另一个一致状态
  • 隔离性(Isolation):一个事务的执行不会被其他事务干扰
  • 持久性(Durability):事务一旦提交,其结果将永久保存在数据库中

事务的状态

  • 活跃状态(Active):事务正在执行
  • 部分提交状态(Partially Committed):事务的最后一条语句执行完毕,但结果尚未写入磁盘
  • 提交状态(Committed):事务执行成功,结果已写入磁盘
  • 失败状态(Failed):事务执行过程中出现错误,需要回滚
  • 终止状态(Aborted):事务已回滚,数据库恢复到事务开始前的状态

事务控制语句

基本事务控制

sql
-- 开始事务(隐式开始)
INSERT INTO employees (employee_id, first_name, last_name, email, hire_date) 
VALUES (207, 'John', 'Doe', 'john.doe@example.com', SYSDATE);

-- 保存点
SAVEPOINT save1;

-- 继续执行事务
UPDATE employees SET salary = salary * 1.1 WHERE employee_id = 207;

-- 回滚到保存点
ROLLBACK TO save1;

-- 提交事务
COMMIT;

-- 回滚整个事务
ROLLBACK;

自动提交

sql
-- 查看自动提交设置
SHOW PARAMETER autocommit;

-- 启用自动提交
SET autocommit ON;

-- 禁用自动提交
SET autocommit OFF;

分布式事务

sql
-- 提交分布式事务
COMMIT FORCE '2PC_TRANSACTION_ID';

-- 回滚分布式事务
ROLLBACK FORCE '2PC_TRANSACTION_ID';

-- 查看分布式事务
SELECT * FROM dba_2pc_pending;

锁机制

锁的作用

  • 保证数据一致性:防止多个事务同时修改同一数据导致数据不一致
  • 实现事务隔离:确保事务的隔离性,防止脏读、不可重复读和幻读
  • 协调并发访问:合理分配资源,提高并发性能

锁的类型

按锁的粒度分类

  1. 行级锁(Row-Level Lock)

    • 粒度最小的锁,锁定单行数据
    • 并发性能最高,冲突概率最低
    • 自动获取,无需显式请求
    • 适合 OLTP 环境
  2. 页级锁(Block-Level Lock)

    • 锁定数据块,包含多行数据
    • 粒度比行级锁大,并发性能较低
    • Oracle 不直接使用页级锁,而是通过行级锁实现类似功能
  3. 表级锁(Table-Level Lock)

    • 锁定整个表
    • 粒度最大,并发性能最低
    • 适合批量操作和数据维护
    • 可以显式请求

按锁的模式分类

  1. 共享锁(Share Lock, S)

    • 允许其他事务读取数据,但不允许修改
    • 用于 SELECT FOR UPDATE 等语句
    • 兼容其他共享锁,但不兼容排他锁
  2. 排他锁(Exclusive Lock, X)

    • 不允许其他事务读取或修改数据
    • 用于 INSERT、UPDATE、DELETE 等语句
    • 不兼容任何其他锁
  3. 更新锁(Update Lock, U)

    • 介于共享锁和排他锁之间
    • 用于 UPDATE 语句的初始阶段
    • 兼容共享锁,但不兼容其他更新锁和排他锁
  4. 意向锁(Intention Lock)

    • 表示事务打算在更细粒度上获取锁
    • 包括意向共享锁(IS)和意向排他锁(IX)
    • 用于表级锁和行级锁之间的协调

Oracle 特定锁类型

  1. 行共享锁(Row Share, RS)

    • 允许其他事务读取和修改表中的其他行
    • 用于 SELECT FOR UPDATE 语句
  2. 行排他锁(Row Exclusive, RX)

    • 允许其他事务读取表,但不允许排他锁定表
    • 用于 INSERT、UPDATE、DELETE 等语句
  3. 共享行排他锁(Share Row Exclusive, SRX)

    • 允许其他事务读取表,但不允许修改表
    • 用于 CREATE INDEX 语句
  4. 表排他锁(Table Exclusive, X)

    • 完全锁定表,不允许任何其他事务访问
    • 用于 ALTER TABLE、DROP TABLE 等语句

锁的获取与释放

自动获取锁

  • SELECT 语句:默认不获取锁,使用一致性读
  • SELECT FOR UPDATE 语句:获取行级共享锁
  • INSERT、UPDATE、DELETE 语句:获取行级排他锁
  • CREATE INDEX 语句:获取表级共享行排他锁
  • ALTER TABLE、DROP TABLE 语句:获取表级排他锁

显式获取锁

sql
-- 显式获取表级共享锁
LOCK TABLE employees IN SHARE MODE;

-- 显式获取表级排他锁
LOCK TABLE employees IN EXCLUSIVE MODE;

-- 显式获取表级行排他锁
LOCK TABLE employees IN ROW EXCLUSIVE MODE;

-- 显式获取表级行共享锁
LOCK TABLE employees IN ROW SHARE MODE;

-- 显式获取表级共享行排他锁
LOCK TABLE employees IN SHARE ROW EXCLUSIVE MODE;

锁的释放

  • 事务提交(COMMIT)后自动释放
  • 事务回滚(ROLLBACK)后自动释放
  • 会话断开后自动释放
  • 显式解锁(LOCK TABLE ... IN SHARE MODE NOWAIT)

并发控制

并发访问问题

在并发环境下,多个事务同时访问数据库可能会导致以下问题:

  1. 脏读(Dirty Read):一个事务读取了另一个事务未提交的数据
  2. 不可重复读(Non-Repeatable Read):一个事务对同一行数据多次读取,结果不一致
  3. 幻读(Phantom Read):一个事务对同一范围的数据多次查询,结果集的行数不一致
  4. 丢失更新(Lost Update):两个事务同时更新同一行数据,导致其中一个更新丢失

隔离级别

Oracle 支持以下隔离级别:

隔离级别脏读不可重复读幻读Oracle 默认
读未提交(READ UNCOMMITTED)可能可能可能不支持
读已提交(READ COMMITTED)不可能可能可能
可重复读(REPEATABLE READ)不可能不可能可能不支持
串行化(SERIALIZABLE)不可能不可能不可能支持

隔离级别的设置

sql
-- 设置会话级隔离级别
ALTER SESSION SET ISOLATION_LEVEL = SERIALIZABLE;

-- 设置事务级隔离级别
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- 查看当前隔离级别
SELECT s.sid, s.serial#, s.username, t.isolation_level
FROM v$session s, v$transaction t
WHERE s.taddr = t.addr;

Oracle 读一致性

Oracle 采用多版本并发控制(MVCC)机制实现读一致性:

  • 一致性读(Consistent Read):SELECT 语句读取事务开始时的数据版本,不受其他事务影响
  • 当前读(Current Read):SELECT FOR UPDATE、INSERT、UPDATE、DELETE 等语句读取最新的数据版本
  • 回滚段(Undo Segment):存储数据的旧版本,用于实现读一致性和事务回滚

锁的监控与管理

锁的监控

sql
-- 查看当前锁信息
SELECT * FROM v$lock;

-- 查看锁等待情况
SELECT * FROM v$lock_waits;

-- 查看阻塞会话
SELECT 
  bl.session_id AS blocking_session,
  w.session_id AS waiting_session,
  bl.lock_type,
  bl.mode_held,
  bl.mode_requested,
  o.object_name,
  s1.username AS blocking_user,
  s2.username AS waiting_user
FROM 
  dba_lock_internal bl,
  dba_lock_internal w,
  dba_objects o,
  v$session s1,
  v$session s2
WHERE 
  bl.session_id = s1.sid AND
  w.session_id = s2.sid AND
  bl.lock_id1 = w.lock_id1 AND
  bl.lock_id2 = w.lock_id2 AND
  bl.mode_held != 'None' AND
  w.mode_requested != 'None' AND
  bl.block = 1 AND
  w.request = 1 AND
  bl.object_id = o.object_id(+);

-- 查看会话锁信息
SELECT s.sid, s.serial#, s.username, l.type, l.id1, l.id2, l.lmode, l.request
FROM v$session s, v$lock l
WHERE s.sid = l.sid;

锁等待事件

sql
-- 查看锁等待事件
SELECT event, COUNT(*) FROM v$session_wait GROUP BY event ORDER BY 2 DESC;

-- 查看具体等待的会话
SELECT s.sid, s.serial#, s.username, s.event, s.wait_time, s.seconds_in_wait
FROM v$session s
WHERE s.event LIKE '%lock%';

锁的管理

sql
-- 终止阻塞会话
ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;

-- 强制释放锁
ALTER SYSTEM DISCONNECT SESSION 'sid,serial#' POST_TRANSACTION;

-- 使用 DBMS_LOCK 包管理锁
EXEC DBMS_LOCK.REQUEST(lockhandle => 'mylock', lockmode => DBMS_LOCK.X_MODE);
EXEC DBMS_LOCK.RELEASE(lockhandle => 'mylock');

死锁处理

死锁的定义

死锁是指两个或多个事务互相等待对方释放锁,导致所有事务都无法继续执行的状态。

死锁的产生条件

  1. 互斥条件:资源不能被共享,只能被一个事务使用
  2. 请求与保持条件:事务已经持有一个资源,又提出新的资源请求
  3. 不剥夺条件:已分配的资源不能被强行剥夺
  4. 循环等待条件:若干事务之间形成首尾相接的循环等待资源关系

死锁的检测与处理

Oracle 自动检测死锁,并采取以下措施:

  1. 死锁检测:Oracle 定期检查是否存在死锁
  2. 死锁处理:当检测到死锁时,Oracle 会选择一个代价最小的事务进行回滚
  3. 死锁日志:死锁信息会记录在告警日志中

死锁的监控

sql
-- 查看死锁历史
SELECT * FROM v$deadlock_history;

-- 查看最近的死锁信息
SELECT * FROM v$diag_history WHERE message LIKE '%deadlock%';

-- 查看告警日志中的死锁信息
-- Linux: tail -f $ORACLE_BASE/diag/rdbms/orcl/ORCL/trace/alert_ORCL.log
-- Windows: 使用 Windows 事件查看器或查看 alert_ORCL.log 文件

死锁的避免

  1. 合理设计事务:尽量缩短事务的执行时间
  2. 合理设计索引:减少锁的竞争
  3. 使用合理的隔离级别:根据业务需求选择合适的隔离级别
  4. 避免长事务:长事务容易导致锁积累和死锁
  5. 使用 NOWAIT 选项:避免长时间等待锁
  6. 统一访问顺序:多个事务访问资源时,按照相同的顺序访问

事务与锁的性能优化

事务性能优化

  1. 缩短事务时间:尽量减少事务中的 SQL 语句数量,避免在事务中执行耗时操作
  2. 避免长事务:长事务会占用大量的回滚段空间,影响系统性能
  3. 合理使用索引:减少锁的竞争,提高查询性能
  4. 使用绑定变量:减少硬解析,提高事务执行速度
  5. 避免在事务中使用 DDL 语句:DDL 语句会隐式提交事务,影响事务的原子性

锁性能优化

  1. 减少锁的持有时间:尽量缩短事务的执行时间,减少锁的持有时间
  2. 减少锁的粒度:优先使用行级锁,避免使用表级锁
  3. 避免锁升级:合理设置锁的参数,避免行级锁升级为表级锁
  4. 使用 NOWAIT 选项:避免长时间等待锁,及时处理锁冲突
  5. 监控锁等待情况:定期监控锁等待情况,及时处理锁冲突
  6. 使用并行执行:对于批量操作,使用并行执行可以提高性能

回滚段优化

sql
-- 查看回滚段使用情况
SELECT segment_name, tablespace_name, r.status, r.blocks, r.extents
FROM dba_rollback_segs r, v$rollname n
WHERE r.segment_name = n.name;

-- 查看回滚段统计信息
SELECT * FROM v$rollstat;

-- 设置回滚段大小
ALTER ROLLBACK SEGMENT rbs01 ONLINE;
ALTER ROLLBACK SEGMENT rbs01 RESIZE 100M;

-- 创建大回滚段
CREATE ROLLBACK SEGMENT rbs_large TABLESPACE undo INITIAL 100M NEXT 100M MAXEXTENTS UNLIMITED;

版本差异

版本事务与锁特性
Oracle 11g增强了回滚段管理,引入自动 undo 管理
Oracle 12c支持多租户环境的事务管理,增强了锁监控功能
Oracle 19c优化了死锁检测算法,增强了并行事务性能
Oracle 23c引入了新的并发控制机制,支持更细粒度的锁

常见问题(FAQ)

Q1: 如何查看当前的锁等待情况?

A1: 使用以下查询:

sql
SELECT 
  bl.session_id AS blocking_session,
  w.session_id AS waiting_session,
  bl.lock_type,
  bl.mode_held,
  bl.mode_requested,
  o.object_name,
  s1.username AS blocking_user,
  s2.username AS waiting_user
FROM 
  dba_lock_internal bl,
  dba_lock_internal w,
  dba_objects o,
  v$session s1,
  v$session s2
WHERE 
  bl.session_id = s1.sid AND
  w.session_id = s2.sid AND
  bl.lock_id1 = w.lock_id1 AND
  bl.lock_id2 = w.lock_id2 AND
  bl.mode_held != 'None' AND
  w.mode_requested != 'None' AND
  bl.block = 1 AND
  w.request = 1 AND
  bl.object_id = o.object_id(+);

Q2: 如何处理死锁?

A2: 处理死锁的步骤:

  1. 查看死锁信息:SELECT * FROM v$deadlock_history;
  2. 分析死锁原因:查看涉及的表、索引和 SQL 语句
  3. 终止阻塞会话:ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;
  4. 优化应用程序:修改应用程序逻辑,避免死锁

Q3: 如何避免锁等待?

A3: 避免锁等待的方法:

  1. 缩短事务时间
  2. 减少锁的持有时间
  3. 使用合理的索引
  4. 使用 NOWAIT 选项
  5. 统一访问顺序
  6. 避免长事务

Q4: 如何设置隔离级别?

A4: 设置隔离级别的方法:

sql
-- 会话级
ALTER SESSION SET ISOLATION_LEVEL = SERIALIZABLE;

-- 事务级
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

Q5: 如何查看事务的隔离级别?

A5: 查看事务隔离级别的方法:

sql
SELECT s.sid, s.serial#, s.username, t.isolation_level
FROM v$session s, v$transaction t
WHERE s.taddr = t.addr;

Q6: 如何优化回滚段?

A6: 回滚段优化的方法:

  1. 使用自动 undo 管理:ALTER SYSTEM SET undo_management = AUTO SCOPE=SPFILE;
  2. 合理设置 undo 表空间大小:ALTER SYSTEM SET undo_tablespace = undo SCOPE=SPFILE;
  3. 设置合理的 undo 保留时间:ALTER SYSTEM SET undo_retention = 3600 SCOPE=SPFILE;
  4. 监控回滚段使用情况:SELECT * FROM v$rollstat;

相关命令速查

命令用途
COMMIT提交事务
ROLLBACK回滚事务
SAVEPOINT创建保存点
ROLLBACK TO savepoint回滚到保存点
LOCK TABLE显式锁定表
ALTER SESSION SET ISOLATION_LEVEL设置隔离级别
SET TRANSACTION ISOLATION LEVEL设置事务隔离级别
SELECT * FROM v$lock查看锁信息
SELECT * FROM v$lock_waits查看锁等待情况
SELECT * FROM v$deadlock_history查看死锁历史
ALTER SYSTEM KILL SESSION终止会话
DBMS_LOCK.REQUEST请求锁
DBMS_LOCK.RELEASE释放锁

总结

事务与锁机制是 Oracle 数据库并发控制的核心,理解事务和锁的工作原理对于 DBA 进行日常运维、性能调优和故障诊断至关重要。DBA 应该根据实际业务需求和系统资源,选择合适的隔离级别,优化事务设计,监控锁等待情况,及时处理死锁,以确保数据库的稳定高效运行。通过合理的事务设计和锁管理,可以提高数据库的并发性能,减少锁冲突,避免死锁,保证数据的一致性和完整性。