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. 把 balance=1000 写入 **Buffer Pool(内存)**
-- 2. 把旧版本 balance=900 写入 **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_ID=100(当前事务 ID)
-- 2. **ReadView 4 步判断**:
--    - trx_id (100) == creator_trx_id (100) ✅ 自己改的,**可以读**
-- 3. 所以读到 balance=1000

⚠️ 关键点

  • 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_ID=100
-- 同一时刻,另一事务:
SELECT balance FROM account WHERE id = 1;  -- 看不到 1000
-- 因为另一事务的 ReadView 看不到 trx_id=100(未提交)

MySQL InnoDB 的核心机制

  • **不存"事务内未提交的私有数据"**到内存
  • 当前行的最新状态 就是 DB_TRX_ID 指向的事务的修改
  • 谁能看,由 ReadView 决定

三、Oracle 真实原理(真的读内存

3.1 关键事实:Oracle 事务内未提交数据真的在内存里
复制代码
-- Oracle 实际行为
BEGIN;
UPDATE account SET balance = 1000 WHERE id = 1;  -- 改了
-- Oracle 实际做了什么?
-- 1. **把 balance=1000 写入 PGA(Process Global Area,进程私有内存)**
-- 2. **不写回磁盘**(不刷新到数据块)
-- 3. 数据块 SCN 没变(还指向旧版本)

SELECT balance FROM account WHERE id = 1;  -- ✅ 看到 1000
-- Oracle 实际做了什么?
-- 1. **优先读 PGA 内存**(事务私有)
-- 2. 找到 balance=1000
-- 3. 返回 1000

⚠️ 关键点

  • Oracle **真的把"事务内未提交的修改"**存在内存里
  • 这是和 MySQL 最大的差异 ------Oracle 事务是有状态的
  • MySQL 事务是无状态的(不看内存,看 ReadView)
3.2 Oracle 的内存分层
复制代码
┌─────────────────────────────────────────────┐
│  Oracle 内存架构(事务视角)                   │
├─────────────────────────────────────────────┤
│                                              │
│  ┌──────────────────────────────────┐        │
│  │  PGA(Process Global Area)       │ ← 进程私有内存
│  │  ├─ 当前事务未提交的修改         │
│  │  ├─ 临时表                       │
│  │  └─ 排序区                       │
│  └──────────────────────────────────┘        │
│           ↓ 优先读                            │
│  ┌──────────────────────────────────┐        │
│  │  SGA(System Global Area)        │ ← 系统共享内存
│  │  ├─ Buffer Cache(数据块缓存)    │
│  │  ├─ Shared Pool                  │
│  │  └─ Redo Log Buffer              │
│  └──────────────────────────────────┘        │
│           ↓ 二次读                            │
│  ┌──────────────────────────────────┐        │
│  │  磁盘                            │ ← 最后读
│  │  ├─ Data Files                    │
│  │  └─ Undo Segment(独立回滚段)    │
│  └──────────────────────────────────┘        │
└─────────────────────────────────────────────┘

Oracle 读优先级PGA(事务私有内存) > SGA(共享缓存) > 磁盘

四、MySQL vs Oracle 内存机制对比

维度 MySQL InnoDB Oracle
事务内未提交数据 不在内存私有区 在 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_ID)
  • Oracle 是"物理层"控制(PGA 内存 + SCN)

五、同一段 SQL 在两个 DB 的执行路径对比

复制代码
-- 老哥同一段 SQL:UPDATE 后立即 SELECT
BEGIN;
UPDATE account SET balance = 1000 WHERE id = 1;
SELECT balance FROM account WHERE id = 1;  -- 看到 1000
5.1 MySQL InnoDB 执行路径
复制代码
1. UPDATE 执行
   ├─ 读取 id=1 的当前行(DB_TRX_ID=99, balance=900)
   ├─ 写入新版本:balance=1000, DB_TRX_ID=100(当前事务 ID)
   ├─ 旧版本写入 Undo Log:balance=900, DB_TRX_PTR
   └─ 当前行 DB_ROLL_PTR 指向 Undo Log

2. SELECT 执行
   ├─ 读取当前行(DB_TRX_ID=100, balance=1000)
   ├─ 创建 ReadView(m_ids=[100], min=100, max=101, creator=100)
   ├─ 4 步判断:trx_id(100) == creator_trx_id(100) ✅
   └─ 返回 balance=1000

⚠️ 注意:InnoDB 没有"内存优先"机制,**只是因为 ReadView 判断逻辑**
5.2 Oracle 执行路径
复制代码
1. UPDATE 执行
   ├─ 读取 id=1 的当前数据块(SCN=1234567, balance=900)
   ├─ 写入新版本:balance=1000 → **PGA 内存**(事务私有)
   ├─ SCN +1 → 1234568
   └─ 数据块未刷新(commit 时才刷)

2. SELECT 执行
   ├─ **优先查 PGA 内存**(事务私有)
   ├─ 找到 balance=1000
   └─ 返回 1000(**不需要查数据块**)

⚠️ 注意:Oracle **真的从内存读**,**不经过 SCN 判断**

六、3 大本质差异

维度 MySQL InnoDB Oracle
事务模型 无状态事务(无私有内存) 有状态事务(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_ID)
  • Oracle = 物理读取(PGA 优先 + SCN)

项目选型

  • 新项目MySQL(无状态事务,分布式友好)
  • 老项目Oracle(有状态事务,OLAP 友好)"

八、记忆口诀

"MySQL 读当前版本,Oracle 读 PGA 内存"

"MySQL 无状态,Oracle 有状态"

"MySQL 逻辑判断(ReadView),Oracle 物理读取(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 InnoDB Oracle
事务状态 无状态(无 PGA 私有内存) 有状态(PGA 私有)
判断机制 逻辑判断(ReadView + DB_TRX_ID) 物理读取(PGA 优先)
数据存储 Buffer Pool 共享 PGA 私有 + SGA 共享

面试话术

"表面相同,原理完全不同

  • MySQL InnoDB 看到自己未提交,不是读内存 ,是 ReadView 4 步判断第 1 步trx_id == creator_trx_id
  • Oracle 看到自己未提交,真的读 PGA 内存(事务私有)

核心差异

  • MySQL = 逻辑判断(ReadView)
  • Oracle = 物理读取(PGA)"
相关推荐
林九生1 小时前
【实用技巧】MySQL 绿色版一键路径更新脚本详解 —— update_path.bat 深度解析
android·数据库·mysql
野生技术架构师1 小时前
从 B+ 树到应用层分表:MySQL 海量数据架构解析
数据库·mysql·架构
Amnesia0_01 小时前
MySQL的事务
数据库·mysql
AC赳赳老秦1 小时前
OpenClaw + 云数据库运维:自动备份、扩容、迁移 RDS/MySQL 云数据库
运维·开发语言·数据库·人工智能·python·mysql·openclaw
TDengine (老段)2 小时前
TDengine 物理计划生成 — 算子下沉、Exchange 与 Subplan 切分
大数据·数据库·物联网·时序数据库·tdengine·涛思数据
swordbob2 小时前
MYSQL RR 解决“脏读+不可重复读“和“幻读“的本质区别
数据库·mysql
IvorySQL2 小时前
PostgreSQL 全球对话:开源链接世界,共建共治共享
数据库·postgresql·开源
Nontee2 小时前
新手数据库进阶:大白话图解四大隔离级别与底层机制
数据库·oracle
dishugj2 小时前
【YashanDB 认证】我的崖山数据库初体验:从陌生到上手的成长之路
数据库