MySQL事务ACID原理

MySQL事务ACID原理

引言

转账、下单、扣库存这类多步操作必须保证要么全成功要么全回滚,靠的就是数据库事务。ACID是事务四个特性的缩写,理解它才能用对事务、排查并发问题。

四个特性

A 原子性 Atomicity
事务内操作不可分割,全部成功提交,失败则整体回滚到事务前状态。InnoDB靠undo log回滚日志实现,记录修改前旧值,出错反向撤销。

C 一致性 Consistency
事务前后数据库从一个合法状态转到另一个合法状态,约束(主键、外键、唯一、余额非负)始终不被破坏。一致性是最终目标,靠ABD共同保障。

I 隔离性 Isolation
并发事务之间互不干扰,隔离级别从低到高:读未提交、读提交、可重复读(InnoDB默认)、串行化,用锁和MVCC实现。

D 持久性 Durability
事务一旦提交,修改永久落盘,宕机也不丢。靠redo log重做日志,先写日志再刷盘,崩溃后重放恢复。

事务用法示例

START TRANSACTION;
UPDATE account SET money = money - 100 WHERE id = 1;
UPDATE account SET money = money + 100 WHERE id = 2;
COMMIT;
-- 出错则 ROLLBACK;

PHP中配合PDO:

$pdo->beginTransaction();
try {
    $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();
}

注意事项

  • 事务不要包太长,避免长时间持锁;
  • 只对必须一致的操作开事务;
  • 一致性靠应用校验和数据库约束共同保证;
  • 隔离级别越高并发越低,按业务权衡。

总结

ACID:原子性靠undo log、持久性靠redo log、隔离性靠锁与MVCC、一致性是目标。转账扣库存等关键链路用事务包裹,注意控制事务长度和隔离级别。


emer 发布于  2026-10-18 07:28 

MySQL 事务隔离级别

MySQL 事务隔离级别

事务隔离是数据库的重要概念,搞懂4种级别很有必要。

1. 事务的ACID

  • 原子性(Atomicity): 要么全成功,要么全失败
  • 一致性(Consistency): 事务前后数据一致
  • 隔离性(Isolation): 多个事务互不影响
  • 持久性(Durability): 提交后永久保存

2. 4种隔离级别

1)读未提交(Read Uncommitted)

  • 能读到未提交的数据
  • 有脏读、不可重复读、幻读问题
  • 性能最好

2)读提交(Read Committed)

  • 只能读到已提交的数据
  • 解决脏读
  • 有不可重复读、幻读问题
  • Oracle默认级别

3)可重复读(Repeatable Read)

  • 同一个事务里读多次结果一样
  • 解决脏读、不可重复读
  • 有幻读问题
  • MySQL默认级别

4)串行化(Serializable)

  • 事务排队执行
  • 解决所有问题
  • 性能最差

3. 三种问题

脏读:

  • 读到另一个事务未提交的数据
  • 对方回滚了,你读的就是脏数据

不可重复读:

  • 同一个事务里,两次读同一行结果不一样
  • 中间被其他事务修改了

幻读:

  • 同一个事务里,两次查询行数不一样
  • 中间被其他事务插入了

4. 查看和设置

-- 查看当前隔离级别
SELECT @@transaction_isolation;

-- 设置隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

5. 实际使用

  • 一般用默认的可重复读就够了
  • 对一致性要求高的用串行化
  • 不要随便改隔离级别

总结

MySQL事务隔离级别要点:

  • 读未提交:性能最好,问题最多
  • 读提交:解决脏读
  • 可重复读:MySQL默认,解决不可重复读
  • 串行化:最安全,性能最差
  • 理解脏读、不可重复读、幻读

做数据库开发,事务隔离一定要懂。


emer 发布于  2026-10-8 07:30 

MySQL 事务隔离级别

MySQL 事务隔离级别

事务是数据库的重要特性,隔离级别决定了事务之间互相影响的程度。这篇讲清楚四个隔离级别。

1. 事务的 ACID 特性

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

2. 三个并发问题

脏读: 一个事务读到另一个事务还没提交的数据

不可重复读: 同一个事务内,两次读同一行数据结果不一样(被别的事务修改了)

幻读: 同一个事务内,两次查询返回的行数不一样(被别的事务插入/删除了)

3. 四个隔离级别

1)读未提交(Read Uncommitted)

  • 最低级别,能读到未提交的数据
  • 会出现脏读、不可重复读、幻读

2)读提交(Read Committed)

  • 只能读到已提交的数据
  • 解决脏读,但还有不可重复读、幻读
  • Oracle 默认级别

3)可重复读(Repeatable Read)

  • 同一个事务内多次读同一行结果一致
  • 解决脏读、不可重复读,但还有幻读
  • MySQL InnoDB 默认级别

4)串行化(Serializable)

  • 最高级别,事务串行执行
  • 解决所有问题,但性能最差

4. 查看和设置

-- 查看当前隔离级别
SELECT @@transaction_isolation;

-- 设置隔离级别(会话级别)
SET SESSION transaction_isolation = 'READ-COMMITTED';

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

5. InnoDB 的优化

MySQL InnoDB 在可重复读级别下,通过 MVCC(多版本并发控制)和间隙锁,也解决了幻读问题,比标准 SQL 定义的更强。

6. 实际开发怎么选

  • 大多数场景用默认的可重复读就够了
  • 对一致性要求极高的场景(金融),用串行化
  • 报表分析等读多写少的场景,可以调低到读提交提高性能

总结

隔离级别 脏读 不可重复读 幻读 性能
读未提交 有 有 有 最好
读提交 无 有 有 好
可重复读 无 无 无(InnoDB) 一般
串行化 无 无 无 最差

MySQL 默认可重复读,日常开发不用改,理解原理就行。


emer 发布于  2026-10-7 07:21 

MySQL事务隔离级别详解,读提交可重复读串行化

前言

做后端开发的同学肯定都听说过事务隔离级别,但是很多同学搞不清楚读未提交、读提交、可重复读、串行化这四个级别到底有什么区别,也不知道自己项目应该用哪个级别。

很多面试的时候也经常被问到:MySQL的默认隔离级别是什么?能解决什么问题?很多同学答不上来。

这篇文章就把MySQL的事务隔离级别从头到尾讲清楚,看完之后你就彻底搞懂了。

一、先复习一下事务的ACID特性

讲隔离级别之前,先复习一下事务的四个特性,也就是ACID:

  1. 原子性(Atomicity):事务里的操作要么全部成功,要么全部失败回滚
  2. 一致性(Consistency):事务执行前后,数据库的完整性约束没有被破坏
  3. 隔离性(Isolation):多个事务之间互相隔离,不能互相干扰
  4. 持久性(Durability):事务提交之后,修改是永久的

我们今天讲的隔离级别,就是跟第三个特性"隔离性"有关的。隔离性的程度不同,就有了不同的隔离级别。

二、并发事务会带来什么问题

为什么需要隔离级别?因为多个事务同时操作数据库的时候,会出现各种问题。

常见的并发问题有三个:

1. 脏读

脏读就是:一个事务读到了另一个事务还没提交的数据。

比如:

  • 事务A把某条数据从1改成了2,但是还没提交
  • 事务B读了这条数据,读到的是2
  • 然后事务A回滚了,数据又变回了1
  • 事务B读的那个2就是脏数据,这就是脏读

2. 不可重复读

不可重复读就是:一个事务里,两次读同一条数据,结果不一样。

比如:

  • 事务A第一次读某条数据,值是1
  • 事务B把这条数据改成了2,并且提交了
  • 事务A第二次再读这条数据,值变成了2
  • 同一个事务里,两次读同一条数据结果不一样,这就是不可重复读

3. 幻读

幻读就是:一个事务里,两次查询的结果集不一样。

比如:

  • 事务A查询id > 5的记录,查到了5、6两条
  • 事务B插入了一条id=7的记录,并且提交了
  • 事务A再查一次,发现多了一条id=7的
  • 这就是幻读

注意:不可重复读和幻读的区别:

  • 不可重复读:同一条数据,两次读结果不一样(修改导致的)
  • 幻读:查询的结果集变多或者变少了(插入/删除导致的)

三、四个事务隔离级别

为了解决上面说的这三个问题,SQL标准定义了四个隔离级别,级别越高,越能解决问题,但是并发性能越差。

1. 读未提交(Read Uncommitted)

最低的隔离级别。一个事务可以读到另一个事务还没提交的数据。

能解决什么问题: 啥也解决不了
会出现什么问题: 脏读、不可重复读、幻读都可能出现

这个级别基本上没人用,所以就不多说了。

2. 读提交(Read Committed,RC)

一个事务只能读到另一个事务已经提交的数据。

能解决什么问题: 解决了脏读
还会出现什么问题: 不可重复读、幻读

Oracle默认就是这个隔离级别。

3. 可重复读(Repeatable Read,RR)

一个事务里,多次读同一条数据,结果是一样的。

能解决什么问题: 解决了脏读、不可重复读
还会出现什么问题: 幻读

注意:MySQL的InnoDB引擎在RR级别下,通过间隙锁解决了幻读问题,所以MySQL的RR级别实际上可以完全解决幻读。

MySQL默认就是这个隔离级别。

4. 串行化(Serializable)

最高的隔离级别。所有事务一个一个排队执行,完全串行。

能解决什么问题: 脏读、不可重复读、幻读全都解决了
缺点: 性能太差了,并发度最低,基本上没人用

四、四个隔离级别对比表

给大家整理了一个表格,一目了然:

隔离级别 脏读 不可重复读 幻读 性能
读未提交 ❌ 可能出现 ❌ 可能出现 ❌ 可能出现 最好
读提交(RC) ✅ 解决了 ❌ 可能出现 ❌ 可能出现 比较好
可重复读(RR) ✅ 解决了 ✅ 解决了 ✅ 解决了(MySQL InnoDB) 比较差
串行化 ✅ 解决了 ✅ 解决了 ✅ 解决了 最差

五、MySQL默认的隔离级别

很多同学面试的时候被问到"MySQL默认的隔离级别是什么",答不上来。

记住:MySQL InnoDB默认的隔离级别是可重复读(RR)。

注意和Oracle区分一下:Oracle默认的是读提交(RC)。

六、怎么查看当前的隔离级别

登录MySQL之后,执行下面的命令:

-- 查看全局的隔离级别
SELECT @@global.tx_isolation;

-- 查看当前会话的隔离级别
SELECT @@session.tx_isolation;

执行完之后会看到类似这样的结果:

+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+

这就说明当前是可重复读级别。

七、怎么修改隔离级别

临时修改(当前会话)

-- 改成读提交
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 改成可重复读
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

这种修改只对当前连接有效,断开连接之后就恢复了。

全局临时修改

SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;

这种修改对新连接有效,已经存在的连接不受影响,MySQL重启之后就恢复了。

永久修改

要永久修改的话,需要改配置文件。编辑my.cnf,在[mysqld]下面添加:

[mysqld]
transaction-isolation = READ-COMMITTED

修改完之后重启MySQL服务,就永久生效了。

八、实际项目中应该用哪个级别

很多同学问:实际项目中应该用哪个隔离级别?

一般来说:

  • 如果对并发性能要求高:用读提交(RC)就够了,很多互联网公司都是用RC
  • 如果对数据一致性要求高:用可重复读(RR),MySQL默认就是这个
  • 串行化基本上没人用,性能太差了

其实大部分情况下,用MySQL默认的RR就够了,不用特意改。如果并发特别高,可以考虑改成RC,这样间隙锁就没了,并发性能会好很多。

常见坑

坑1:不知道MySQL默认的隔离级别是RR

很多同学以为MySQL默认是读提交,跟Oracle一样,结果面试答错了。记住:MySQL默认是可重复读(RR)。

坑2:把不可重复读和幻读搞混了

很多同学分不清不可重复读和幻读。记住:

  • 不可重复读:同一条数据被修改了,两次读结果不一样
  • 幻读:结果集的行数变多或者变少了,是插入/删除导致的

坑3:以为RR级别解决不了幻读

很多同学以为RR级别解决不了幻读,其实MySQL的InnoDB引擎在RR级别下,通过间隙锁是可以解决幻读的。这一点跟标准SQL不一样,MySQL做了增强。

坑4:修改了隔离级别但是没生效

很多同学用SET GLOBAL改了隔离级别,然后用当前连接查,发现还是原来的级别。这是因为SET GLOBAL只对新连接有效,当前连接还是原来的。要当前连接生效的话,用SET SESSION。

总结

MySQL事务隔离级别的核心知识点:

  1. 并发事务的三个问题:脏读、不可重复读、幻读
  2. 四个隔离级别:读未提交、读提交、可重复读、串行化,级别越高越安全,性能越差
  3. 脏读:读到了别的事务还没提交的数据
  4. 不可重复读:同一个事务里,两次读同一条数据结果不一样(修改导致的)
  5. 幻读:同一个事务里,两次查询的结果集不一样(插入/删除导致的)
  6. MySQL InnoDB默认是可重复读(RR),并且通过间隙锁解决了幻读
  7. 查看隔离级别:SELECT @@tx_isolation
  8. 修改隔离级别:SET SESSION/GLOBAL TRANSACTION ISOLATION LEVEL ...
  9. 实际项目用RR或者RC就够了,串行化性能太差没人用

事务隔离级别是数据库的基础知识点,不管是面试还是实际开发,都是必须掌握的。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-5 08:51 

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 最核心也最容易出问题的部分。很多人写了很多年 SQL,却还是搞不清隔离级别和死锁。本文用最直白的方式讲清楚。

一、事务的 ACID

事务就是一组操作,要么全部成功,要么全部失败。四个特性:

  • A 原子性:一组操作是一个整体,不能拆分
  • C 一致性:事务前后,数据库从一个一致状态到另一个一致状态
  • I 隔离性:多个事务之间互不干扰
  • D 持久性:事务提交后,数据就永久保存了

二、并发问题

多个事务同时操作数据,会出什么问题?

1. 脏读

事务 A 读到了事务 B 还没提交的数据。

事务 A:修改了余额为 1000,但还没提交
事务 B:读到了余额 1000
事务 A:回滚了,余额变回 500
事务 B:拿着 1000 的错误数据继续操作

2. 不可重复读

事务 A 两次读同一行数据,结果不一样。

事务 A:第一次读余额是 500
事务 B:修改了余额为 1000,提交了
事务 A:第二次读余额是 1000

3. 幻读

事务 A 两次查询,结果集的行数不一样。

事务 A:第一次查询 age > 20 的用户,有 10 条
事务 B:插入了一条 age = 25 的用户,提交了
事务 A:第二次查询 age > 20 的用户,有 11 条

三、四种隔离级别

MySQL 用隔离级别来解决这些并发问题:

隔离级别 脏读 不可重复读 幻读
读未提交(READ UNCOMMITTED) 会 会 会
读提交(READ COMMITTED) 不会 会 会
可重复读(REPEATABLE READ) 不会 不会 会(InnoDB 解决了)
串行化(SERIALIZABLE) 不会 不会 不会

MySQL 默认是可重复读(REPEATABLE READ),InnoDB 在这个级别下用间隙锁解决了幻读。

查看当前隔离级别

SELECT @@tx_isolation;

修改隔离级别

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

四、锁的类型

1. 共享锁(S 锁)

读锁,多个事务可以同时持有。

SELECT ... LOCK IN SHARE MODE;

2. 排他锁(X 锁)

写锁,只有一个事务能持有。

SELECT ... FOR UPDATE;

3. 表锁 vs 行锁

  • 表锁:锁住整张表,开销小,但并发低
  • 行锁:锁住一行数据,开销大,但并发高

五、死锁

什么是死锁

两个事务互相等待对方释放锁。

事务 A:锁住了 id = 1,等待 id = 2
事务 B:锁住了 id = 2,等待 id = 1

两个事务都卡住了,谁也不让谁。

怎么避免死锁

  1. 按相同顺序访问表和行:所有事务都按 id 从小到大操作
  2. 事务尽量短:事务越长,锁持有时间越长
  3. 降低隔离级别:隔离级别越低,锁越少
  4. 加索引:没有索引会走表锁,更容易死锁

查看死锁

-- 查看最近一次死锁
SHOW ENGINE INNODB STATUS;

六、实战:转账

-- 开启事务
BEGIN;

-- 扣款(加行锁)
UPDATE account SET balance = balance - 100 WHERE id = 1;

-- 检查余额
SELECT balance FROM account WHERE id = 1;

-- 如果余额不够,回滚
-- ROLLBACK;

-- 加钱
UPDATE account SET balance = balance + 100 WHERE id = 2;

-- 提交
COMMIT;

七、常见坑

  1. 事务太长:锁持有时间太长,容易死锁
  2. 没有索引:行锁变表锁,并发暴跌
  3. 隔离级别太高:用串行化,性能很差
  4. 忘记提交:事务一直开着,锁一直占着

总结

MySQL 事务与锁的核心思路:

  1. 事务保证 ACID,一组操作要么全成功要么全失败
  2. 隔离级别越高越安全,但性能越差
  3. MySQL 默认可重复读,InnoDB 解决了幻读
  4. 行锁比表锁并发高,但一定要有索引
  5. 按相同顺序操作,能避免大部分死锁

搞懂这些,你对 MySQL 的理解就超过 80% 的开发者了。


emer 发布于  2026-10-4 10:11 
'); })();