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 事务隔离级别

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

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 InnoDB和MyISAM引擎区别,怎么选

前言

很多新手同学刚学MySQL的时候,根本不知道存储引擎是什么,建表的时候直接用默认的。其实存储引擎很重要,不同的存储引擎有不同的特点,适合不同的场景。

MySQL最常用的两个存储引擎就是InnoDB和MyISAM。很多同学搞不清楚它们有什么区别,也不知道应该选哪个。这篇文章就把这两个引擎的区别讲清楚,看完之后你就知道怎么选了。

一、什么是存储引擎

存储引擎就是MySQL里,真正存储数据、管理数据的东西。MySQL是把数据和引擎分开的,不同的表可以用不同的存储引擎。

就像你有一个仓库,你可以选择用货架放东西,也可以用箱子放东西,不同的存放方式有不同的特点。存储引擎就是这个"存放方式"。

二、InnoDB和MyISAM的核心区别

我们先看一个对比表,一目了然:

特点 InnoDB MyISAM
事务支持 ✅ 支持 ❌ 不支持
外键支持 ✅ 支持 ❌ 不支持
锁粒度 行锁 表锁
崩溃恢复 ✅ 支持 ❌ 不支持
全文索引 ✅ 5.6以后支持 ✅ 支持
主键索引 聚簇索引 非聚簇索引
读写性能 写性能好,读也不错 读性能好,写性能差

三、InnoDB的特点

InnoDB是MySQL默认的存储引擎,从MySQL 5.5开始,默认就是InnoDB了。

1. 支持事务

这是InnoDB最大的特点。支持ACID事务,也就是原子性、一致性、隔离性、持久性。如果你需要事务,那就必须用InnoDB。

2. 支持行锁

InnoDB的锁粒度是行锁。也就是说,一个事务锁了某一行,其他事务还可以操作表里的其他行。这样并发性能就很好。

3. 支持外键

InnoDB支持外键约束,可以保证数据的一致性。

4. 支持崩溃恢复

InnoDB有redo log,MySQL崩溃之后重启,能够自动恢复数据,不容易丢数据。

5. 聚簇索引

InnoDB的主键索引是聚簇索引,叶子节点直接存数据。这样按主键查询速度很快。

InnoDB适合什么场景

  • 需要事务的场景
  • 读写都比较频繁的场景
  • 对数据一致性要求高的场景
  • 大部分互联网业务场景

四、MyISAM的特点

MyISAM是MySQL早期默认的存储引擎,现在用的越来越少了。

1. 不支持事务

MyISAM不支持事务,执行SQL就是直接执行,没有事务的概念。

2. 表锁

MyISAM的锁粒度是表锁。也就是说,一个操作锁了整张表,其他操作都要等。这样并发性能就很差。

3. 读性能好

MyISAM的读性能很好,因为它的结构简单,查询的时候直接读文件就行。

4. 不支持崩溃恢复

MyISAM没有redo log,MySQL崩溃之后,很可能会损坏数据,需要手动修复。

5. 非聚簇索引

MyISAM的索引是非聚簇索引,叶子节点存的是数据的地址,按主键查询要回表。

MyISAM适合什么场景

  • 读多写少的场景
  • 不需要事务的场景
  • 一些日志表、统计报表表
  • 对数据一致性要求不高的场景

五、怎么选InnoDB和MyISAM

很多同学问:到底应该选哪个?

其实现在大部分场景,直接选InnoDB就对了。因为InnoDB是默认的,功能也全,支持事务、行锁、崩溃恢复,大部分业务都适用。

MyISAM现在基本上只有一些特殊场景才会用,比如:

  • 纯读的表,比如一些静态配置表
  • 一些历史遗留的老项目,原来是用MyISAM的

记住:新项目直接用InnoDB,不用考虑MyISAM。

六、怎么查看表用的是什么引擎

查看某张表的引擎

SHOW TABLE STATUS LIKE '表名';

执行完之后,看Engine那列,就是这张表用的存储引擎。

查看数据库里所有表用的引擎

SELECT TABLE_NAME, ENGINE 
FROM information_schema.TABLES 
WHERE TABLE_SCHEMA = '数据库名';

七、怎么修改表的引擎

如果一张表原来是用MyISAM,想改成InnoDB,可以执行:

ALTER TABLE 表名 ENGINE = InnoDB;

注意:这个操作会把整张表重建一遍,如果表很大的话,会花很长时间,而且会锁表。最好在业务低峰期执行。

常见坑

坑1:以为MyISAM性能更好

很多同学觉得MyISAM读性能好,就想用MyISAM。其实现在InnoDB的性能已经很好了,尤其是读多写少的场景,InnoDB也不差。而且MyISAM不支持事务、表锁,并发高了性能很差。现在新项目直接用InnoDB就对了。

坑2:不知道MySQL默认引擎变了

很多老教程说MySQL默认是MyISAM,其实从MySQL 5.5开始,默认就是InnoDB了。不要被老教程误导。

坑3:用MyISAM做业务表

很多同学不知道MyISAM不支持事务,结果把业务表用MyISAM,出了问题才发现。只要是业务表,需要事务的,一定要用InnoDB。

坑4:修改引擎的时候没注意锁表

很多同学直接在线上执行ALTER TABLE改引擎,结果锁表了,业务直接卡住了。改引擎之前一定要评估表大小,最好在业务低峰期执行,或者用pt-online-schema-change这种工具在线改。

坑5:以为InnoDB一定比MyISAM慢

很多同学觉得InnoDB因为支持事务、行锁,所以性能比MyISAM差。其实不是,现在InnoDB的性能已经非常好了,尤其是读写都有并发的场景,InnoDB的行锁比MyISAM的表锁性能好很多。

总结

InnoDB和MyISAM的核心区别:

  1. InnoDB支持事务,MyISAM不支持
  2. InnoDB是行锁,MyISAM是表锁,InnoDB并发性能好
  3. InnoDB支持崩溃恢复,MyISAM不支持
  4. InnoDB支持外键,MyISAM不支持
  5. InnoDB是聚簇索引,按主键查询快
  6. 现在MySQL默认就是InnoDB,新项目直接用InnoDB
  7. MyISAM只有一些特殊场景才用,比如纯读的表

记住:现在不用纠结选哪个,新项目直接用InnoDB就对了,不用考虑MyISAM。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-5 09:03 

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 
'); })();