MySQL和Oracle关于读未提交的区别
这两段语句虽然表现的结果都是一样的但其实原理是不同的对吗1 MySQL InnoDB事务级 ReadView事务级事务开始时建立 ReadView整个事务期间复用RR或每次新建RC。关键事实MySQL 看不到自己未提交的修改。-- MySQL InnoDB 行为 BEGIN; UPDATE account SET balance 1000 WHERE id 1; -- 改了 SELECT balance FROM account WHERE id 1; -- ⚠️ 看到 1000自己的修改 -- 注意这是自己能看到自己但其他事务看不到 -- 其他事务 SELECT balance FROM account WHERE id 1; -- ⚠️ 看不到 1000未提交2 Oracle语句级 ReadView语句级每个 SQL 语句启动时建立 ReadView。关键事实Oracle 事务内能读取到自己未提交的修改。-- Oracle 行为 BEGIN; UPDATE account SET balance 1000 WHERE id 1; -- 改了 SELECT balance FROM account WHERE id 1; -- ✅ 看到 1000事务内可见 -- 这是因为 Oracle 读 PGA/SGA 内存中的数据优先于 Undo -- 其他事务 SELECT balance FROM account WHERE id 1; -- ⚠️ 看不到 1000未提交一、直接答是的原理不同数据库表面结果底层原理MySQL InnoDB✅ 看到 1000自己的修改⚠️不是读内存是读最新已提交版本InnoDB 不存在事务内未提交可见机制Oracle✅ 看到 1000自己的修改✅是读 PGA/SGA 内存Oracle 事务内未提交数据真的就在内存里表面相同原理不同——这是 4 大差异里最核心的一点。二、MySQL InnoDB 真实原理不是内存优先2.1 关键事实MySQL InnoDB 没有事务内未提交数据可见机制-- MySQL InnoDB 实际行为**注意MySQL 也能看到自己的未提交修改** BEGIN; UPDATE account SET balance 1000 WHERE id 1; -- 改了 -- InnoDB 实际做了什么 -- 1. 把 balance1000 写入 **Buffer Pool内存** -- 2. 把旧版本 balance900 写入 **Undo Log** -- 3. 当前行的 DB_TRX_ID 改为 **当前事务 ID**假设 100 -- 4. DB_ROLL_PTR 指向 Undo Log 里的旧版本 SELECT balance FROM account WHERE id 1; -- 看到 1000 -- InnoDB 实际做了什么 -- 1. 读当前行 → DB_TRX_ID100当前事务 ID -- 2. **ReadView 4 步判断** -- - trx_id (100) creator_trx_id (100) ✅ 自己改的**可以读** -- 3. 所以读到 balance1000⚠️ 关键点MySQL 看到自己未提交的修改不是因为读内存是因为ReadView 4 步判断的第 1 步trx_id creator_trx_id→ **自己改的返回当前版本当前版本就是最新的已修改的版本——不是内存是数据行的当前状态2.2 MySQL InnoDB 看不到内存中的数据看到的是数据行当前状态-- MySQL InnoDB 的内存数据Buffer Pool不是事务私有 -- 多个事务可能同时访问同一行 -- 当前行的 DB_TRX_ID 决定谁能看 BEGIN; UPDATE account SET balance 1000 WHERE id 1; -- 当前行 DB_TRX_ID100 -- 同一时刻另一事务 SELECT balance FROM account WHERE id 1; -- 看不到 1000 -- 因为另一事务的 ReadView 看不到 trx_id100未提交MySQL InnoDB 的核心机制**不存事务内未提交的私有数据**到内存当前行的最新状态就是DB_TRX_ID指向的事务的修改谁能看由 ReadView 决定三、Oracle 真实原理真的读内存3.1 关键事实Oracle 事务内未提交数据真的在内存里-- Oracle 实际行为 BEGIN; UPDATE account SET balance 1000 WHERE id 1; -- 改了 -- Oracle 实际做了什么 -- 1. **把 balance1000 写入 PGAProcess Global Area进程私有内存** -- 2. **不写回磁盘**不刷新到数据块 -- 3. 数据块 SCN 没变还指向旧版本 SELECT balance FROM account WHERE id 1; -- ✅ 看到 1000 -- Oracle 实际做了什么 -- 1. **优先读 PGA 内存**事务私有 -- 2. 找到 balance1000 -- 3. 返回 1000⚠️ 关键点Oracle **真的把事务内未提交的修改**存在内存里这是和 MySQL 最大的差异——Oracle 事务是有状态的MySQL 事务是无状态的不看内存看 ReadView3.2 Oracle 的内存分层┌─────────────────────────────────────────────┐ │ Oracle 内存架构事务视角 │ ├─────────────────────────────────────────────┤ │ │ │ ┌──────────────────────────────────┐ │ │ │ PGAProcess Global Area │ ← 进程私有内存 │ │ ├─ 当前事务未提交的修改 │ │ │ ├─ 临时表 │ │ │ └─ 排序区 │ │ └──────────────────────────────────┘ │ │ ↓ 优先读 │ │ ┌──────────────────────────────────┐ │ │ │ SGASystem Global Area │ ← 系统共享内存 │ │ ├─ Buffer Cache数据块缓存 │ │ │ ├─ Shared Pool │ │ │ └─ Redo Log Buffer │ │ └──────────────────────────────────┘ │ │ ↓ 二次读 │ │ ┌──────────────────────────────────┐ │ │ │ 磁盘 │ ← 最后读 │ │ ├─ Data Files │ │ │ └─ Undo Segment独立回滚段 │ │ └──────────────────────────────────┘ │ └─────────────────────────────────────────────┘Oracle 读优先级PGA事务私有内存 SGA共享缓存 磁盘四、MySQL vs Oracle 内存机制对比维度MySQL InnoDBOracle事务内未提交数据❌不在内存私有区✅在 PGA事务私有内存读取优先级Buffer Pool共享PGA私有 SGA共享 磁盘能看到自己未提交原因ReadView 第 1 步trx_id creator_trx_id直接读 PGA 内存其他事务能看到自己未提交❌ 不能❌ 不能PGA 是事务私有的事务回滚Undo Log丢弃 PGA 内存 Undo Segment持久化时机提交时刷脏页提交时刷 SGA 写 Redo核心差异MySQL 是逻辑层控制ReadView DB_TRX_IDOracle 是物理层控制PGA 内存 SCN五、同一段 SQL 在两个 DB 的执行路径对比-- 老哥同一段 SQLUPDATE 后立即 SELECT BEGIN; UPDATE account SET balance 1000 WHERE id 1; SELECT balance FROM account WHERE id 1; -- 看到 10005.1 MySQL InnoDB 执行路径1. UPDATE 执行 ├─ 读取 id1 的当前行DB_TRX_ID99, balance900 ├─ 写入新版本balance1000, DB_TRX_ID100当前事务 ID ├─ 旧版本写入 Undo Logbalance900, DB_TRX_PTR └─ 当前行 DB_ROLL_PTR 指向 Undo Log 2. SELECT 执行 ├─ 读取当前行DB_TRX_ID100, balance1000 ├─ 创建 ReadViewm_ids[100], min100, max101, creator100 ├─ 4 步判断trx_id(100) creator_trx_id(100) ✅ └─ 返回 balance1000 ⚠️ 注意InnoDB 没有内存优先机制**只是因为 ReadView 判断逻辑**5.2 Oracle 执行路径1. UPDATE 执行 ├─ 读取 id1 的当前数据块SCN1234567, balance900 ├─ 写入新版本balance1000 → **PGA 内存**事务私有 ├─ SCN 1 → 1234568 └─ 数据块未刷新commit 时才刷 2. SELECT 执行 ├─ **优先查 PGA 内存**事务私有 ├─ 找到 balance1000 └─ 返回 1000**不需要查数据块** ⚠️ 注意Oracle **真的从内存读****不经过 SCN 判断**六、3 大本质差异维度MySQL InnoDBOracle事务模型无状态事务无私有内存有状态事务PGA 私有内存判断机制逻辑判断ReadView DB_TRX_ID物理判断PGA 优先读数据可见性所有事务共享同一份 Buffer Pool事务私有 PGA 共享 SGA七、面试话术表面相同原理不同MySQL InnoDB看到自己未提交的修改不是读内存是ReadView 4 步判断的第 1 步trx_id creator_trx_id→ 自己改的返回当前版本。InnoDB 事务是无状态的没有事务私有内存。Oracle看到自己未提交的修改是真的读 PGA 内存事务私有。Oracle 事务是有状态的PGA 存了事务内未提交的数据。核心差异MySQL 逻辑判断ReadView DB_TRX_IDOracle 物理读取PGA 优先 SCN项目选型新项目→MySQL无状态事务分布式友好老项目→Oracle有状态事务OLAP 友好八、记忆口诀MySQL 读当前版本Oracle 读 PGA 内存MySQL 无状态Oracle 有状态MySQL 逻辑判断ReadViewOracle 物理读取PGA表面相同原理不同新项目用 MySQL无状态分布式友好老项目用 Oracle有状态 OLAP 友好 总结问MySQL 看到 1000 vs Oracle 看到 1000原理相同吗答完全不同的原理。数据库表面结果真实原理MySQL InnoDB✅ 看到 1000不是读内存——是 ReadView 4 步判断第 1 步trx_id creator_trx_id→ 自己改的返回当前版本Oracle✅ 看到 1000真的读 PGA 内存事务私有3 大本质差异维度MySQL InnoDBOracle事务状态无状态无 PGA 私有内存有状态PGA 私有判断机制逻辑判断ReadView DB_TRX_ID物理读取PGA 优先数据存储Buffer Pool 共享PGA 私有 SGA 共享面试话术表面相同原理完全不同MySQL InnoDB看到自己未提交不是读内存是ReadView 4 步判断第 1 步trx_id creator_trx_idOracle看到自己未提交真的读 PGA 内存事务私有核心差异MySQL 逻辑判断ReadViewOracle 物理读取PGA