mysql数据库中事务的ACID特性解释

来源:这里教程网 时间:2026-02-28 20:45:27 作者:

事务的原子性靠什么保证?

原子性不是靠 SQL 语句写得“看起来一起执行”就成立的,而是由

InnoDB
存储引擎通过
Undo Log
Redo Log
协同实现的。你写
BEGIN
COMMIT
ROLLBACK
,只是告诉引擎“我要开始/结束一个事务”,真正回滚时,InnoDB 会按
Undo Log
中记录的反向操作(比如
INSERT
对应
DELETE
UPDATE
对应反向
UPDATE
)逐条还原数据。

如果你在事务中执行了
DROP TABLE
ALTER TABLE
,会**立即触发隐式提交**,后续
ROLLBACK
无效——DDL 是事务“终结者”
innodb_flush_log_at_trx_commit
设为
0
2
虽能提升写性能,但机器断电时可能丢事务;生产环境建议保持默认
1
大事务(如批量更新百万行)容易撑爆
undo log
空间,导致
rollback
极慢甚至卡死,应拆分执行

一致性不是数据库自动兜底的

很多人误以为“用了事务,数据就天然一致”。其实

Consistency
是 ACID 中最依赖业务逻辑的一环:数据库只保证约束(如主键、外键、
CHECK
)、不破坏已定义的规则;但业务层面的“钱总额不变”“库存不能为负”这类逻辑,必须靠你写对 SQL 顺序、加锁、校验条件来保障。

转账场景下,仅靠
UPDATE account SET balance = balance - 100 WHERE id = 1
+
UPDATE account SET balance = balance + 100 WHERE id = 2
不够——若第一条成功、第二条失败且没
ROLLBACK
,钱就凭空消失了
没有外键或
ENUM
约束时,
status
字段被误设为
'pending_pay'
(而合法值只有
'paid'
/
'canceled'
),事务照样提交成功,但业务已不一致
SELECT ... FOR UPDATE
在读取余额后加行锁,能防止并发扣款超支,这是手动保一致的关键动作

隔离性失效的典型现场

默认的

REPEATABLE READ
隔离级别看似安全,但在高并发下仍可能遇到幻读(新插入行被读到),而
READ COMMITTED
虽避免幻读,却带来不可重复读问题。是否出问题,取决于你有没有在事务内做“范围条件读 + 后续写入”这类操作。

执行
SELECT * FROM order WHERE status = 'unpaid'
得到 5 条,接着想把这 5 条全更新为
'processing'
,但如果别人在你查询后插入了第 6 条未支付订单,
REPEATABLE READ
下你查不到它,但更新语句会命中它(InnoDB 的 next-key lock 机制决定),结果更新了 6 条——这就是“幻读”的实际影响
SELECT ... LOCK IN SHARE MODE
FOR UPDATE
显式加锁,比单纯依赖隔离级别更可控
应用层缓存(如 Redis)和数据库事务不同步时,即使数据库强一致,用户看到的仍是脏数据——隔离性只管 DB 层,不管应用层

持久性≠永不丢失,要看刷盘配置

所谓“提交即永久”,前提是

Redo Log
已落盘。而是否落盘,由
innodb_flush_log_at_trx_commit
决定。别只看文档说“ACID 保证持久”,线上出问题时,往往栽在这个参数上。

设为
0
:崩溃前一秒提交的事务大概率丢失(log 只在 buffer,未刷盘也未交由 OS)
设为
2
:MySQL 挂了不丢,但 OS 崩溃或断电仍可能丢(log 在 OS cache,未 fsync 到磁盘)
设为
1
(默认):每次
COMMIT
都强制
fsync
,最安全,但写入吞吐受限——SSD 上影响小,机械盘上明显
如果用了
binlog
+
Redo Log
双写,配合
sync_binlog=1
,才能支撑主从强一致和崩溃恢复

事务的 ACID 不是开关一开就自动生效的魔法,每个特性背后都有明确的机制、配置和使用边界。最容易被忽略的是:一致性靠代码逻辑兜底,隔离性靠锁+隔离级别协同,而持久性最终取决于你敢不敢让 Redo Log 多等那一次磁盘 IO。

相关推荐