MySQL 事务隔离级别

MySQL 事务隔离级别详解

事务是数据库并发控制的基础,搞懂四个隔离级别,才能解决脏读、幻读问题。

1. 事务 ACID 特性

  • 原子性(Atomicity):要么全成功,要么全回滚
  • 一致性(Consistency):事务前后数据一致
  • 隔离性(Isolation):事务之间互不干扰
  • 持久性(Durability):提交后永久保存

2. 四种隔离级别

MySQL 有四种隔离级别,从上到下越来越严格:

读未提交(READ UNCOMMITTED)

  • 可以读到别人还没提交的数据
  • 会出现脏读
  • 几乎没人用

读提交(READ COMMITTED)

  • 只能读到别人已经提交的数据
  • 解决了脏读,但会出现不可重复读
  • Oracle、SQL Server 默认级别

可重复读(REPEATABLE READ)

  • 同一个事务里,读多次结果都一样
  • 解决了脏读、不可重复读
  • MySQL InnoDB 默认级别
  • InnoDB 用间隙锁解决了幻读

串行化(SERIALIZABLE)

  • 事务一个一个排队执行
  • 最安全,但性能最差
  • 几乎没人用

3. 三种读问题

脏读:读到了别人还没提交的数据,别人回滚了,你读到的就是脏数据。

不可重复读:同一个事务里,两次读同一行数据,结果不一样(别人中间改了并提交了)。

幻读:同一个事务里,两次查同一个范围,行数不一样(别人中间插了新行)。

4. 查看和设置隔离级别

-- 查看当前隔离级别
SELECT @@global.tx_isolation;
SELECT @@session.tx_isolation;

-- 设置全局隔离级别
SET GLOBAL tx_isolation = 'READ-COMMITTED';

-- 设置当前会话隔离级别
SET SESSION tx_isolation = 'READ-COMMITTED';

5. 实际应用建议

  • 默认就好:MySQL 默认的可重复读已经够用
  • 高并发场景:可以降到读提交,提升并发性能
  • 金融级场景:用串行化,绝对安全
  • 大多数业务:默认级别就行,不用折腾

6. 代码示例

// PDO 事务示例
try {
    $pdo->beginTransaction();

    $pdo->exec("UPDATE account SET money = money - 100 WHERE id = 1");
    $pdo->exec("UPDATE account SET money = money + 100 WHERE id = 2");

    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollBack();
    echo "转账失败:" . $e->getMessage();
}

总结

MySQL 事务隔离级别核心:

  • 读未提交:脏读
  • 读提交:不可重复读
  • 可重复读:默认级别,解决幻读
  • 串行化:最安全最慢

大多数业务用默认的可重复读就够了。


emer 发布于  2026-10-4 11:05 

MySQL 锁机制详解

MySQL 锁机制详解

MySQL 锁是并发控制的核心,搞懂锁,才能避免死锁和性能问题。

1. 表锁 vs 行锁

表锁:锁整个表,开销小,并发低。

  • MyISAM 用的就是表锁
  • 适合读多写少的场景

行锁:锁单行数据,开销大,并发高。

  • InnoDB 用的就是行锁
  • 适合写多的场景

2. 共享锁 vs 排他锁

共享锁(S锁):读锁,多个读不互斥。

SELECT ... LOCK IN SHARE MODE

排他锁(X锁):写锁,一个写独占。

SELECT ... FOR UPDATE

3. 间隙锁

InnoDB 用间隙锁防止幻读。锁的是一个范围,不是具体行。

比如你查 id > 10 AND id < 20,间隙锁会把这个区间锁住,别人插不进来。

4. 死锁

两个事务互相等对方的锁,都不放,就死锁了。

避免死锁的方法:

  • 按相同顺序访问表
  • 事务尽量小,快速提交
  • 降低隔离级别
  • 加索引,减少行锁范围

5. 查看锁状态

-- 查看当前锁
SHOW OPEN TABLES WHERE In_use > 0;

-- 查看 InnoDB 锁
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;

-- 查看锁等待
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

6. 行锁什么时候会变成表锁

行锁失效就会升级成表锁:

  • 没用索引
  • 索引失效
  • 全表扫描

所以加索引不仅是为了查得快,也是为了锁得准。

总结

MySQL 锁的核心:

  • InnoDB 默认行锁,MyISAM 是表锁
  • 共享锁读、排他锁写
  • 间隙锁防幻读
  • 死锁要避免:顺序访问、小事务、加索引

搞懂这些,数据库并发问题基本能解决一半。


emer 发布于  2026-10-4 10:46