跳到主要内容

ACID 特性

问题

事务的 ACID 特性分别是什么?数据库是如何保证每一个特性的?

答案

ACID 概述

特性英文含义实现机制(InnoDB)
原子性Atomicity事务要么全做,要么全不做undo log
一致性Consistency事务前后数据满足约束由 A + I + D 共同保证
隔离性Isolation并发事务互不干扰MVCC + 锁
持久性Durability提交后数据不丢失redo log + WAL
核心关系

一致性是目标,其他三个是手段。原子性保证操作不会只做一半,隔离性保证并发不会互相干扰,持久性保证提交的数据不会丢失 —— 三者共同保障数据从一个一致状态转换到另一个一致状态。

原子性(Atomicity)

含义:事务中的操作要么全部执行成功,要么全部回滚,不存在执行了一半的中间状态。

实现机制 —— undo log

undo log 记录了数据修改前的值(逆操作):

操作undo log 记录
INSERT记录新行的主键,回滚时 DELETE
DELETE记录完整行数据,回滚时 INSERT
UPDATE记录被修改列的旧值,回滚时还原
-- 示例:转账事务
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- undo log: UPDATE accounts SET balance = (原值) WHERE id = 1
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- undo log: UPDATE accounts SET balance = (原值) WHERE id = 2

-- 如果第二条 UPDATE 失败 → ROLLBACK
-- InnoDB 按照 undo log 反向还原两条记录
ROLLBACK;

一致性(Consistency)

含义:事务执行前后,数据库从一个一致状态转换到另一个一致状态。所有数据完整性约束(主键、外键、唯一约束、CHECK 约束、业务规则)不被破坏。

保证方式

层面手段
数据库层面原子性(不会半做)+ 隔离性(不被干扰)+ 持久性(不丢数据)
约束层面PRIMARY KEY、UNIQUE、FOREIGN KEY、NOT NULL、CHECK
应用层面业务逻辑正确性(如转账总金额不变)需要应用代码保证
一致性 vs 其他三个特性

原子性、隔离性、持久性是数据库提供的技术保障。一致性更偏向"最终结果的正确性",既需要数据库的技术保障,也需要应用层的业务逻辑正确。例如,数据库可以保证转账不会中断,但"等额转入转出"的规则需要应用代码来保证。

隔离性(Isolation)

含义:多个事务并发执行时,每个事务的执行不受其他事务的干扰,就像单独执行一样。

实现机制

机制作用
MVCC读操作不加锁,通过快照实现读写不冲突
锁机制写写冲突通过行锁、间隙锁等解决
隔离级别定义不同程度的隔离强度

详细内容见 隔离级别MVCC 原理

持久性(Durability)

含义:事务一旦提交,对数据的修改就是永久的,即使系统崩溃也不会丢失。

实现机制 —— redo log + WAL

WAL(Write-Ahead Logging,预写日志)

核心思想:先写日志,再写数据

  1. 数据修改先写入 redo log(顺序写磁盘,很快)
  2. 再修改内存中的数据页(Buffer Pool)
  3. 脏页异步刷回磁盘(随机写,较慢)
  4. 如果在脏页刷盘前崩溃,通过 redo log 恢复

为什么用 redo log 而不直接写数据文件?

直接写数据文件写 redo log
随机 IO(数据页分散在磁盘各处)顺序 IO(日志追加写)
每次写一个完整页(16KB)只写修改部分(几十字节)

redo log vs binlog

对比项redo logbinlog
层级InnoDB 引擎层MySQL Server 层
内容物理日志(页的修改)逻辑日志(SQL 语句)
大小固定大小,循环写追加写,无大小限制
作用崩溃恢复(Crash Recovery)主从复制、数据恢复

详细内容见 MySQL 日志系统

ACID 实现总结


常见面试问题

Q1: ACID 分别是什么?数据库如何保证?

答案

  • 原子性(A):事务全做或全不做。InnoDB 通过 undo log 实现,回滚时反向执行 undo log 恢复数据
  • 一致性(C):事务前后数据满足所有约束。本质上是其他三个特性的最终目标,加上约束机制共同保证
  • 隔离性(I):并发事务互不干扰。InnoDB 通过 MVCC(读写不冲突)和 (写写冲突)实现
  • 持久性(D):提交后数据不丢失。InnoDB 通过 redo log + WAL 机制实现

Q2: undo log 和 redo log 的区别?

答案

对比项undo logredo log
目的保证原子性(回滚)保证持久性(崩溃恢复)
内容数据修改前的旧值数据修改后的新值
写入时机修改数据前事务提交时
生命周期事务结束后可能保留(MVCC 需要)循环覆盖写

两者配合:提交时 redo log 保证修改不丢,回滚时 undo log 恢复原值。

Q3: 什么是 WAL?为什么需要它?

答案

WAL(Write-Ahead Logging)= 先写日志,再写数据。

原因:

  1. 性能:redo log 是顺序写(追加),数据文件是随机写。顺序写比随机写快几个数量级
  2. 安全:redo log 写入成功后,即使数据页还没刷盘,崩溃后也能通过 redo log 恢复
  3. 批量化:内存中的脏页可以积累后批量刷盘,减少磁盘 IO 次数

Q4: 一致性和隔离性有什么关系?

答案

一致性是最终目标,隔离性是达成一致性的手段之一。

如果没有隔离性,并发事务可能读到中间状态的数据(脏读),或前后两次读到不同数据(不可重复读),导致基于这些数据做出的操作违反业务一致性。

隔离级别越高,一致性保障越强,但并发性能越低。实际应用中需要根据业务场景选择合适的隔离级别。

Q5: 如果系统在事务提交后、数据刷盘前崩溃,数据会丢吗?

答案

不会丢失。原因:

  1. 事务提交时,redo log 已经写入磁盘(innodb_flush_log_at_trx_commit = 1 时)
  2. 系统重启后,InnoDB 执行崩溃恢复:
    • 扫描 redo log,找到已提交但未刷盘的修改
    • 重放这些修改到数据页
  3. 同时用 undo log 回滚未提交事务的修改

这就是 WAL 的核心价值 —— redo log 落盘 = 数据安全。

相关链接