Oracle的READ COMMITTED默认生效,依赖MVCC与语句级快照;MySQL需显式配置且为行级快照,不防幻读,UPDATE行为也不同。

Oracle 的 READ COMMITTED 是怎么自动生效的
Oracle 默认就是 READ COMMITTED,你连显式设置都不用做——只要没手动 SET TRANSACTION ISOLATION LEVEL SERIALIZABLE,每次新会话启动就自动落在这个级别上。它靠的是“多版本并发控制(MVCC)+ 语句级快照”:每个 SELECT 执行时,只看到该语句开始前已提交的数据,不被后续事务干扰。
常见错误现象:ORA-01555: snapshot too old 就是这个机制的副作用——长时间运行的查询,依赖的回滚段被覆盖了。别怪隔离级别,要查 UNDO_RETENTION 和 undo 表空间是否够用。
实操建议:
- 不用改任何配置,但得确认没在应用里用
SET TRANSACTION意外切到SERIALIZABLE - 长事务或报表类查询,加
AS OF TIMESTAMP或AS OF SCN显式控制快照点,比依赖默认行为更可控 -
SELECT FOR UPDATE仍会阻塞,因为写锁不因隔离级别降低而消失
MySQL 的 READ COMMITTED 需要 InnoDB + 显式配置
MySQL 默认是 REPEATABLE READ,想切到 READ COMMITTED 必须手动设。而且只对 InnoDB 有效——MyISAM 根本不支持事务隔离级别,设了也白设。
关键差异在“快照粒度”:Oracle 是语句级,MySQL InnoDB 在 READ COMMITTED 下是“行级快照”,每次 SELECT 都重新读取最新已提交版本,所以同一事务内两次 SELECT 可能结果不同(非锁定读不复用快照)。
实操建议:
- 全局改:修改
my.cnf加transaction_isolation = 'READ-COMMITTED',重启生效 - 会话级临时改:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED - 注意 binlog 格式:如果用
STATEMENT,READ COMMITTED可能导致主从不一致,推荐ROW或MIXED
为什么 MySQL 开启 READ COMMITTED 后幻读没消失
很多人以为 READ COMMITTED 能防幻读,其实不能。幻读指“同一范围条件查出新插入的行”,而 MySQL 的 READ COMMITTED 仅避免脏读和不可重复读,**不加间隙锁(gap lock)**,所以其他事务仍可向当前查询范围插入新行。
典型场景:你执行 SELECT * FROM t WHERE id > 100 得到 10 行;别人立刻 INSERT INTO t VALUES (101, ...) 并提交;你再查一次,就多出一行——这就是幻读,且在 READ COMMITTED 下合法发生。
实操建议:
- 真要防幻读,要么切回
REPEATABLE READ(InnoDB 默认,靠间隙锁阻塞插入),要么手动加SELECT ... FOR UPDATE或LOCK IN SHARE MODE - 业务上更稳妥的做法是:用唯一约束 + 插入前校验,而不是依赖隔离级别拦住所有并发写
跨数据库迁移时最容易忽略的隐性行为差异
Oracle 的 READ COMMITTED 对 UPDATE/DELETE 语句也按语句级快照走,即更新时只看到语句开始前的数据;MySQL 的 READ COMMITTED 下,UPDATE 会先做 SELECT 再更新,中间可能被别的事务改过——这会导致“更新丢失”风险变高,尤其在无主键或条件宽泛的更新中。
比如:两个事务同时执行 UPDATE t SET status='done' WHERE status='pending',在 Oracle 中,后执行的会基于自己语句开始时的快照判断哪些行满足条件;在 MySQL READ COMMITTED 下,它可能查到已被第一个事务改掉的行,然后报错或静默跳过(取决于是否匹配到)。
实操建议:
- 迁移前跑一遍
EXPLAIN或EXPLAIN PLAN,确认 WHERE 条件是否走索引——没索引的范围更新,在 MySQLREAD COMMITTED下更容易出竞态 - 涉及“查-改”逻辑的代码,别依赖隔离级别自动串行化,加
SELECT ... FOR UPDATE显式锁住目标行 - 测试阶段重点压测“并发更新同一批数据”的路径,Oracle 过得去,MySQL 可能当场暴露
真正麻烦的不是设哪个级别,而是同一段 SQL 在两边执行时,底层快照时机、锁范围、甚至返回行数都可能不同——得盯着执行计划和锁监控看,不能只信文档里的定义。










