Skip to content

Oracle 并发控制

并发控制概述

并发控制是数据库管理系统的核心功能之一,用于管理多个用户同时访问数据库时的资源竞争问题。Oracle 数据库采用多种机制来确保数据一致性和系统性能之间的平衡。

并发控制的重要性

  • 确保数据一致性:防止脏读、不可重复读和幻读
  • 提高系统吞吐量:允许多个事务同时执行
  • 减少锁竞争:优化资源利用率
  • 避免死锁:确保事务能够顺利完成

Oracle 并发控制机制

Oracle 数据库主要采用以下并发控制机制:

  • 多版本并发控制(MVCC)
  • 锁机制
  • 事务隔离级别
  • 快照隔离

多版本并发控制(MVCC)

MVCC 基本原理

Oracle 是最早实现 MVCC(Multi-Version Concurrency Control)的数据库之一,通过为每个数据行维护多个版本来实现高并发访问。

核心概念

  • 撤销段(Undo Segments):存储数据的旧版本
  • 系统更改号(SCN):全局唯一的事务标识符
  • 读一致性:每个查询看到的数据都是事务开始时的一致视图

MVCC 工作机制

  1. 事务修改数据时,Oracle 将旧数据复制到撤销段
  2. 新数据写入数据块,并记录 SCN
  3. 查询时,Oracle 根据查询开始时的 SCN 从撤销段读取相应版本的数据
  4. 事务提交后,Oracle 会在适当的时候清理不再需要的撤销数据

MVCC 在不同 Oracle 版本中的改进

版本改进内容
11g引入自动撤销管理(AUM)增强
12c支持可插拔数据库的撤销隔离
19c优化撤销段管理,提高大并发下的性能
21c增强多租户环境下的撤销管理

乐观锁与悲观锁

悲观锁

悲观锁假设冲突总是会发生,因此在事务开始时就锁定资源。

实现方式

  • SELECT ... FOR UPDATE:锁定查询结果集
  • LOCK TABLE:锁定整个表
  • FOR UPDATE NOWAIT:避免锁等待

应用场景

  • 高并发写操作
  • 关键业务数据更新
  • 长事务
sql
-- 使用悲观锁锁定特定行
SELECT * FROM orders WHERE order_id = 123 FOR UPDATE;

-- 不等待锁
SELECT * FROM orders WHERE order_id = 123 FOR UPDATE NOWAIT;

乐观锁

乐观锁假设冲突很少发生,只在提交时检查数据是否被修改。

实现方式

  • 版本号字段:每次更新递增版本号
  • 时间戳字段:记录最后修改时间
  • Oracle 内置机制:利用 SCN 或 ORA_ROWSCN

应用场景

  • 读多写少的系统
  • 短事务
  • 分布式系统
sql
-- 版本号实现乐观锁
UPDATE products 
SET quantity = quantity - 1, version = version + 1 
WHERE product_id = 456 AND version = 3;

-- 检查更新是否成功
IF SQL%ROWCOUNT = 0 THEN
  -- 处理并发冲突
END IF;

锁类型与锁模式

Oracle 锁类型

锁类型描述适用场景
DML 锁保护数据完整性INSERT、UPDATE、DELETE 操作
DDL 锁保护数据库对象结构CREATE、ALTER、DROP 操作
内部锁保护数据库内部结构内存结构、文件访问等

锁模式

锁模式描述兼容模式
共享锁(S)允许并发读,阻止写与 S、RS 兼容
排他锁(X)阻止所有并发访问仅与 None 兼容
行共享锁(RS)允许其他事务并发访问与 S、RS、RX 兼容
行排他锁(RX)允许并发读,阻止排他访问与 S、RS、RX 兼容
共享行排他锁(SRX)限制并发写操作与 S、RS 兼容

并发控制性能调优

识别并发问题

常见并发性能指标

  • 锁等待时间(v$lock_wait)
  • 阻塞会话数(v$session_blockers)
  • 死锁数量(v$deadlock)
  • 事务等待时间(v$transaction)

监控并发问题

sql
-- 查看阻塞会话
SELECT 
  s1.sid AS blocking_session,
  s2.sid AS blocked_session,
  l1.type AS lock_type,
  l1.lmode AS lock_mode,
  l2.request AS requested_mode
FROM 
  v$lock l1,
  v$lock l2,
  v$session s1,
  v$session s2
WHERE 
  l1.block = 1 AND
  l2.request > 0 AND
  l1.id1 = l2.id1 AND
  l1.id2 = l2.id2 AND
  l1.sid = s1.sid AND
  l2.sid = s2.sid;

-- 查看死锁信息
SELECT * FROM v$deadlock;

并发性能优化策略

应用设计优化

  • 缩小事务范围:尽量减少事务持续时间
  • 避免长事务:将大事务拆分为多个小事务
  • 合理设计索引:减少锁竞争
  • 使用批量操作:减少DML语句数量

锁定策略优化

  • 优先使用乐观锁:减少锁竞争
  • 避免锁定整个表:使用行级锁
  • 合理设置隔离级别:根据业务需求选择
  • 使用 NOWAIT 或 WAIT 子句:避免无限等待

数据库参数优化

  • undo_retention:调整撤销数据保留时间
  • parallel_degree_policy:优化并行执行
  • cursor_sharing:减少硬解析
  • statistics_level:确保统计信息准确

不同 Oracle 版本的并发优化特性

版本并发优化特性
11g自动撤销管理增强、结果缓存
12c自适应执行计划、多租户并发控制
19c实时统计信息、自动索引优化
21c混合分区表、增强的并行处理

隔离级别与并发控制

Oracle 支持的隔离级别

隔离级别描述脏读不可重复读幻读
READ COMMITTED读取已提交的数据防止允许允许
SERIALIZABLE串行化执行防止防止防止
READ ONLY只读事务防止防止防止
READ WRITE读写事务防止允许允许

设置隔离级别

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

-- 或在事务开始时设置
BEGIN
  SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
  -- 事务操作
END;

常见并发问题及解决方案

死锁

死锁产生原因

  • 循环等待:事务A等待事务B的锁,事务B等待事务A的锁
  • 锁升级:行锁升级为表锁
  • 长事务:事务持有锁时间过长

死锁解决方案

  • 避免循环依赖:统一访问资源的顺序
  • 减少锁持有时间:尽快提交事务
  • 使用 NOWAIT 子句:避免无限等待
  • 监控死锁:定期检查 v$deadlock 视图

锁等待

锁等待产生原因

  • 大量并发写操作
  • 不合理的锁定策略
  • 长事务
  • 索引设计不合理

锁等待解决方案

  • 优化查询语句:减少锁定范围
  • 使用更细粒度的锁:行级锁替代表级锁
  • 增加并发度:调整初始化参数
  • 监控锁等待:使用 AWR 报告分析

读一致性问题

读一致性产生原因

  • 事务隔离级别设置不当
  • 撤销段不足
  • 长查询跨越多个事务

读一致性解决方案

  • 合理设置 undo_retention 参数
  • 增加撤销表空间大小
  • 优化长查询:减少查询范围
  • 使用适当的隔离级别

最佳实践

开发阶段最佳实践

  1. 设计合理的事务边界:尽量减少事务持续时间
  2. 优先使用乐观锁:减少锁竞争
  3. 合理设计索引:确保查询使用索引,减少全表扫描
  4. 避免锁定整个表:使用行级锁
  5. 使用批量操作:减少DML语句数量

运维阶段最佳实践

  1. 监控并发性能:定期检查锁等待、阻塞会话等指标
  2. 调整撤销表空间:根据业务需求设置合适的大小
  3. 优化数据库参数:调整与并发相关的参数
  4. 定期收集统计信息:确保执行计划准确
  5. 进行压力测试:评估系统在高并发下的性能

常见问题(FAQ)

Q1: Oracle 是如何实现读一致性的?

A: Oracle 通过多版本并发控制(MVCC)实现读一致性。当事务修改数据时,旧数据会被保存到撤销段,查询时根据查询开始时的 SCN 从撤销段读取相应版本的数据,确保每个查询看到的都是事务开始时的一致视图。

Q2: 乐观锁和悲观锁有什么区别?

A: 乐观锁假设冲突很少发生,只在提交时检查数据是否被修改;悲观锁假设冲突总是会发生,在事务开始时就锁定资源。乐观锁适用于读多写少的场景,悲观锁适用于高并发写操作的场景。

Q3: 如何避免死锁?

A: 避免死锁的方法包括:统一访问资源的顺序,减少锁持有时间,使用 NOWAIT 子句避免无限等待,监控死锁并及时处理,合理设计事务边界。

Q4: 如何监控 Oracle 中的锁等待?

A: 可以通过查询 v$lock_wait、v$session_blockers、v$transaction 等视图来监控锁等待情况。也可以使用 AWR 报告、ASH 报告等工具进行深入分析。

Q5: 不同 Oracle 版本的并发控制有什么区别?

A: 随着 Oracle 版本的升级,并发控制机制不断增强。例如,11g 引入了自动撤销管理增强,12c 支持可插拔数据库的撤销隔离,19c 优化了撤销段管理,21c 增强了多租户环境下的并发控制。

Q6: 如何选择合适的隔离级别?

A: 选择隔离级别需要根据业务需求和性能要求综合考虑。READ COMMITTED 是默认隔离级别,适用于大多数场景;SERIALIZABLE 提供最高级别的一致性,但性能较低;READ ONLY 适用于只读查询场景。

Q7: 长事务对并发性能有什么影响?

A: 长事务会持有锁很长时间,导致其他事务等待,增加锁竞争,降低系统吞吐量。此外,长事务还会导致撤销段增长,影响读一致性查询的性能。

Q8: 如何优化高并发下的 Oracle 性能?

A: 优化高并发下的 Oracle 性能可以从以下几个方面入手:优化应用设计,缩小事务范围,合理使用锁定策略,优化数据库参数,调整撤销表空间,定期收集统计信息,进行压力测试等。