MySQL InnoDB 默认隔离级别是 REPEATABLE READ
启动一个新连接后,执行
SELECT @@transaction_isolation;或
SELECT @@tx_isolation;(旧版本),返回值通常是
'REPEATABLE-READ'。这是 InnoDB 的默认设置,不是 MySQL Server 层的全局默认(Server 层默认是
REPEATABLE READ,但实际生效由引擎决定)。
READ COMMITTED
能避免不可重复读,但需注意 MVCC 行为变化
InnoDB 在
READ COMMITTED下,每个
SELECT语句都会创建新的快照(即“语句级快照”),而
REPEATABLE READ是“事务级快照”——第一次
SELECT就确定了整个事务可见的数据版本。 这意味着在
READ COMMITTED中,同一事务内两次
SELECT可能返回不同结果(如果其他事务已提交修改)
UPDATE/
DELETE语句在
READ COMMITTED下只看到已提交版本,不会像
REPEATABLE READ那样基于事务初态做一致性读 某些场景下,
READ COMMITTED的锁范围更小(例如非唯一条件更新时,可能不加间隙锁),但具体取决于查询是否命中索引
SERIALIZABLE
会强制将所有普通 SELECT
转为 SELECT ... LOCK IN SHARE MODE
这不是加个表锁那么简单。InnoDB 在
SERIALIZABLE下,对没有显式加锁的
SELECT语句自动加上共享锁(S 锁),导致并发读写阻塞明显升高。 例如:事务 A 执行
SELECT * FROM t WHERE id = 10;,事务 B 同时执行
UPDATE t SET x=1 WHERE id = 10;,B 会被阻塞直到 A 提交或回滚 即使查询走的是二级索引,也可能触发临键锁(next-key lock),进一步限制并发 除非业务强依赖绝对顺序一致性,否则一般不建议全局启用
SERIALIZABLE
修改隔离级别要注意作用域和生效时机
隔离级别可以在会话级、全局级甚至单条语句级设置,但行为有差异:
会话级:SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;—— 只影响当前连接后续事务 全局级:
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;—— 新建连接继承该值,已有连接不受影响 事务级:
START TRANSACTION WITH CONSISTENT SNAPSHOT;不指定隔离级别,仍按当前会话设置;若要覆盖,必须在
START TRANSACTION前用
SET TRANSACTION ISOLATION LEVEL ...MySQL 8.0+ 支持在
SELECT语句末尾加
FOR UPDATE或
LOCK IN SHARE MODE,但这属于加锁语义,不改变事务隔离级别本身
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; START TRANSACTION; SELECT * FROM orders WHERE status = 'pending'; -- 使用 READ COMMITTED 快照 UPDATE orders SET status = 'processing' WHERE id = 123; COMMIT;
真正容易被忽略的是:隔离级别只约束“读可见性”和“锁行为”,它不能替代应用层的逻辑校验。比如两个事务同时读到同一行并各自更新,在
READ COMMITTED下可能产生覆盖写,这不是隔离级别能解决的——得靠乐观锁(
version字段)或应用层重试机制。
