ITPub博客

首页 > 数据库 > Oracle > oracle和MySQL的ACID详解

oracle和MySQL的ACID详解

原创 Oracle 作者:dingjun123 时间:2026-05-10 22:06:05 0 删除 编辑

      数据库ACID机制的ANSI标准并未完全实现C和I,可能存在不可重复读和幻读问题,也会发生第二类丢失更新,三大数据库oracle,pg,MySQL的隔离级别和实现也不一样,其中MySQL有四大隔离级别,而且MySQL的RR实现了可重复读和幻读,但是存在快照读与当前读混合使用出现紊乱问题,MySQL的RR是默认隔离级别,主要使用read view快照机制,record lock,nextkey lock,gap lock防止幻读,而oracle只有RC和serializable两种隔离级别,主要是行锁,PG有三大隔离级别,其中RR不能防止幻读,快照机制会判断快照,可能会报错。如何实现RC下防止不可重复读和幻读,数据库侧实现是有两种基本策略可用于防止不可重复读和幻像读。在第一种策略(基于锁的并发控制)中,事务 2 在事务 1 提交或回滚后提交。它产生串行调度 T1, T2。在另一种策略(多版本并发控制)中,事务 2 立即提交,而先于事务 2 启动的事务 1 继续对事务 1 启动时创建的数据库旧快照进行操作,当事务 1 最终尝试提交时,如果提交的结果等同于串行调度T1, T2,则提交事务 1;否则,存在提交冲突,事务 1 将因序列化失败而回滚。 在基于锁的并发控制下,如果在执行 SELECT 时未获取读锁,或者在执行 SELECT 后立即释放受影响行上获取的锁,则可能会发生不可重复读和幻像读。在多版本并发控制下,如果放松了受提交冲突影响的事务必须回滚的要求,则可能会发生不可重复读和幻像读。程序编写采用乐观锁,悲观锁....所以真正实现ACID需要数据库侧隔离级别,锁机制,MVCC机制,完整性约束以及编程配合来解决



在数据库系统中,实现ACID特性需要结合隔离级别、并发控制机制及应用程序的协作。针对RC(Read Committed)隔离级别下防止不可重复读和幻读的需求,需深入理解其挑战及可行的策略,以下是分步解析:


一、RC隔离级别的默认行为与问题

默认行为:RC允许读取已提交的数据,每次SELECT会获取最新快照,可能导致不可重复读(同一事务内两次读取结果不同)和幻读(新行插入导致范围查询结果变化)。


第二类丢失更新:两个事务读取并修改同一数据,后提交的事务覆盖前事务的结果,RC下需额外机制防止。


二、防止不可重复读和幻读的数据库侧策略

1. 基于锁的并发控制(LBCC)

显式加锁:


行级锁(SELECT FOR UPDATE):在查询时对目标行加排他锁,阻止其他事务修改,直到当前事务提交。


间隙锁(Gap Locks):锁定范围内的间隙,阻止插入新行(如MySQL的InnoDB在RC下默认不启用间隙锁,需手动指定)。


实现要点:锁需保持到事务结束,避免中途释放导致其他事务介入。


示例(MySQL):



START TRANSACTION;

SELECT * FROM accounts WHERE balance > 1000 FOR UPDATE; -- 显式加锁

-- 执行更新操作

COMMIT;


2. 多版本并发控制(MVCC)结合冲突检测

快照隔离增强:


提交时冲突检查:即使RC下允许读取最新提交,可在事务提交时验证数据是否被修改(类似乐观锁)。


可串行化快照隔离(SSI):PostgreSQL的可串行化级别使用SSI检测读写依赖,但在RC下需自定义逻辑,例如通过触发器或应用层版本号检查。


实现示例:



BEGIN;

SELECT version FROM table WHERE id = 1; -- 获取当前版本

-- 执行业务逻辑...

UPDATE table SET data = 'new', version = version + 1 

WHERE id = 1 AND version = :old_version; -- 提交时检查版本

COMMIT;


三、应用层协作机制

1. 乐观锁(Optimistic Locking)

版本号或时间戳:在数据表中添加版本字段,更新时校验版本是否变更。



UPDATE orders SET status = 'shipped', version = version + 1 

WHERE order_id = 123 AND version = 5;

适用场景:低冲突环境中减少锁竞争,但需处理提交失败的重试逻辑。


2. 悲观锁(Pessimistic Locking)

提前加锁:通过SELECT FOR UPDATE在事务开始时锁定关键资源。



BEGIN;

SELECT * FROM inventory WHERE product_id = 456 FOR UPDATE; -- 锁定行

UPDATE inventory SET stock = stock - 1 WHERE product_id = 456;

COMMIT;

适用场景:高冲突环境,确保操作的原子性。


四、数据库实现差异与适配策略

MySQL:


RC下幻读问题:默认使用MVCC,但无间隙锁,需显式加锁(如SELECT ... FOR UPDATE配合WHERE条件锁定范围)。


示例:防止插入新订单号:



SELECT * FROM orders WHERE order_number BETWEEN 1000 AND 2000 FOR UPDATE;

PostgreSQL:


RC下的不可重复读:每次读最新提交数据,但可通过SSI或应用层版本控制避免。


显式锁建议:使用FOR SHARE或FOR UPDATE锁定行。


Oracle:


行锁主导:RC下通过行级锁阻止写冲突,但需显式加锁防止丢失更新。


示例:



SELECT * FROM employees WHERE department_id = 10 FOR UPDATE;

五、总结:综合解决方案

数据库侧:


在RC下结合显式锁(行锁、间隙锁)限制并发修改。


启用完整性约束(如唯一索引防幻读插入)。


应用侧:


乐观锁处理版本冲突,悲观锁提前抢占资源。


事务设计短小,减少锁持有时间。


跨数据库适配:


根据数据库特性调整锁策略(如MySQL需手动间隙锁,Oracle依赖行锁)。


通过数据库机制与应用逻辑的协同,即使在RC隔离级别下,也能有效防止不可重复读和幻读,逼近更高的隔离性,同时平衡性能与一致性需求。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/7655508/viewspace-3117487/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2008-05-07

  • 博文量
    45
  • 访问量
    90557