乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 数据库基础与安装

    • MySQL 简介
    • MySQL 5.6 到 5.7 到 8.0 关键差异速查
    • 安装 MySQL 5.7
    • 连接与断开服务器
    • 创建数据库
    • 创建数据表
    • 数据库与数据表
    • 加载数据
    • 获取数据库信息
    • 批处理模式
    • SHOW 语句汇总
    • FLUSH 与 RESET 语句
    • my.cnf 核心参数
    • 字符集与排序规则
  • 第2章 SQL基础查询

    • SELECT
    • WHERE
    • ORDER BY
    • LIMIT
    • COUNT
    • 聚合函数
    • 比较运算符
    • 逻辑运算符
    • 算术运算符
    • 模式匹配
    • NULL 值处理
    • UPDATE
    • DELETE
    • REPLACE
    • SELECT INTO
  • 第3章 数据类型与运算符

    • 数值类型
    • 字符串类型
    • 日期时间类型
    • BIT 类型
    • ENUM 类型
    • SET 类型
    • JSON 类型
    • 类型转换
  • 第4章 函数与表达式

    • 字符串函数
    • 数值函数
    • 日期函数
    • 全文检索函数
  • 第5章 高级查询与子查询

    • JOIN
    • 子查询
    • UNION
    • GROUP BY
    • HAVING
    • DISTINCT
  • 第6章 表与索引

    • 数据定义语言
    • 修改表结构
    • 视图
    • 修改视图与检查选项
    • 外键
    • 索引
    • 唯一索引
    • 复合索引
    • 存储引擎对比
    • 分区表
    • 第一范式与第二范式
    • 第三范式与 BC 范式
    • 反范式设计
  • 第7章 存储过程与函数

    • 存储过程
    • 存储函数
    • 变量
    • 流程控制
    • 游标
    • 预处理语句
  • 第8章 事务与锁

    • 事务
    • 事务隔离级别
    • 锁机制
    • MVCC
    • 死锁专题分析
    • LOCK TABLES
    • XA 事务
  • 第9章 用户管理与安全

    • 用户管理
    • 权限管理
    • 角色
    • SQL 注入防范
  • 第10章 性能优化入门

    • 执行计划
    • 索引优化
    • 查询优化
    • 查询优化器提示
    • 慢查询日志
    • InnoDB 深入机制
    • InnoDB 专项优化
    • Performance Schema
    • sys Schema
  • 第11章 复制与高可用

    • 主从复制原理
    • 半同步复制配置
    • binlog 开启与 point-in-time 恢复
    • mysqldump 全库备份
    • mysqldump 单表与条件备份
    • mysqldump 恢复与导入
    • xtrabackup 全量热备
    • xtrabackup 准备与恢复
    • xtrabackup 增量与流式备份
  • 第12章 触发器与事件

    • 触发器
    • 事件调度器
  • 参考

    • MySQL 5.7 专业术语大全
    • MySQL 5.7 关键字与保留字大全

事务

导学

转账操作"A 账户扣款 + B 账户收款"必须同时成功或同时失败——这就是事务的典型场景。事务是数据库保证数据一致性的核心机制,也是面试中最常被问到的数据库概念之一。

定义

事务(Transaction):一组逻辑上的数据库操作单元,这些操作要么全部成功执行并提交(Commit),要么全部不执行并回滚(Rollback)。事务具有ACID四大特性。

ACID 详解

特性含义通俗解释
原子性(Atomicity)事务是最小执行单元,不可再分要么全做,要么全不做
一致性(Consistency)事务执行前后,数据库处于一致状态转账前后,总余额不变
隔离性(Isolation)并发事务之间相互隔离你的事务看不到别人未提交的数据
持久性(Durability)提交后数据永久保存就算数据库崩溃,已提交的数据也不会丢失

事务控制语句

START TRANSACTION;  -- 或 BEGIN
-- 执行 SQL 操作
COMMIT;             -- 提交事务
ROLLBACK;           -- 回滚事务
SAVEPOINT sp1;      -- 设置保存点
ROLLBACK TO sp1;    -- 回滚到保存点

SQL 示例

准备测试数据

CREATE TABLE employees (
    emp_id INT PRIMARY KEY AUTO_INCREMENT,
    emp_name VARCHAR(20),
    dept VARCHAR(20),
    score DECIMAL(5,2)
);

INSERT INTO employees (emp_name, dept, score) VALUES
('大翔', '技术部', 100),
('白歌', '技术部', NULL);

CREATE TABLE logs (
    log_id INT PRIMARY KEY AUTO_INCREMENT,
    emp_id INT,
    action VARCHAR(20),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

employees 表初始数据:

emp_idemp_namedeptscore
1大翔技术部100
2白歌技术部NULL

logs 表初始状态:空表

场景一:事务提交演示

操作过程

-- 查看事务前的数据状态
SELECT * FROM employees;

操作前的数据状态:

emp_idemp_namedeptscore
1大翔技术部100
2白歌技术部NULL
START TRANSACTION;

-- 1. 查询大翔的当前分数
SELECT score FROM employees WHERE emp_id = 1 FOR UPDATE;

查询结果:

score
100
-- 2. 大翔分数减少 50
UPDATE employees SET score = score - 50 WHERE emp_id = 1;

-- 3. 白歌分数设置为 50(直接赋值,避开 NULL 计算)
UPDATE employees SET score = 50 WHERE emp_id = 2;

-- 4. 记录操作日志
INSERT INTO logs (emp_id, action)
VALUES (1, '分数调整:大翔 -50,白歌 =50');

COMMIT;

操作后的数据状态:

SELECT * FROM employees;
emp_idemp_namedeptscore
1大翔技术部50
2白歌技术部50
SELECT * FROM logs;
log_idemp_idactioncreated_at
11分数调整:大翔 -50,白歌 =502024-01-15 10:00:00

结果解读:

  • 四个操作(查分数、大翔减分、白歌赋分、记日志)要么全部成功(COMMIT),要么全部回滚(ROLLBACK)
  • 如果第 3 步失败,前 2 步不会留下"大翔扣了分但白歌没加上"的中间状态
  • FOR UPDATE 锁住大翔的行,防止并发修改(详见《锁机制》文档)
  • 操作前后总分数保持 100(50 + 50),体现了一致性

场景二:事务回滚演示

操作过程

-- 查看初始状态
SELECT * FROM employees;

操作前的数据状态:

emp_idemp_namedeptscore
1大翔技术部50
2白歌技术部50
START TRANSACTION;

-- 1. 大翔分数增加 50
UPDATE employees SET score = score + 50 WHERE emp_id = 1;

-- 2. 白歌部门调整为产品部
UPDATE employees SET dept = '产品部' WHERE emp_id = 2;

-- 此时查看中间状态(在当前事务内可见)
SELECT * FROM employees;

事务中的中间状态:

emp_idemp_namedeptscore
1大翔技术部100
2白歌产品部50
-- 3. 假设发现业务错误,执行回滚
ROLLBACK;

-- 查看回滚后的状态
SELECT * FROM employees;

操作后的数据状态(回滚后):

emp_idemp_namedeptscore
1大翔技术部50
2白歌技术部50

结果解读:

  • ROLLBACK 后,事务中的所有修改都被撤销
  • 大翔的分数和白歌的部门恢复到事务开始前的状态
  • 这体现了事务的原子性:要么全成功,要么全失败

场景三:使用保存点

操作过程

-- 查看初始状态
SELECT * FROM employees;
SELECT * FROM logs;

操作前的数据状态:employees 表已有大翔和白歌,logs 表为空

START TRANSACTION;

-- 1. 记录第一条日志
INSERT INTO logs (emp_id, action) VALUES (1, '操作开始');

-- 查看当前日志
SELECT * FROM logs;

当前日志状态:

log_idemp_idactioncreated_at
11操作开始2024-01-15 10:00:00
-- 2. 设置保存点
SAVEPOINT after_start;

-- 3. 记录第二条日志(中间操作)
INSERT INTO logs (emp_id, action) VALUES (2, '中间操作');

-- 查看当前日志
SELECT * FROM logs;

当前日志状态:

log_idemp_idactioncreated_at
11操作开始2024-01-15 10:00:00
22中间操作2024-01-15 10:00:01
-- 4. 假设发现中间操作有误,回滚到保存点
ROLLBACK TO SAVEPOINT after_start;

-- 查看回滚后的日志状态
SELECT * FROM logs;

回滚后的日志状态:

log_idemp_idactioncreated_at
11操作开始2024-01-15 10:00:00
-- 5. 继续其他操作(第一条日志保留,中间操作被回滚)
INSERT INTO logs (emp_id, action) VALUES (1, '操作完成');

COMMIT;

-- 查看最终日志状态
SELECT * FROM logs;

最终日志状态:

log_idemp_idactioncreated_at
11操作开始2024-01-15 10:00:00
31操作完成2024-01-15 10:00:02

结果解读:

  • SAVEPOINT after_start 在插入第一条日志后创建了一个"检查点"
  • ROLLBACK TO SAVEPOINT after_start 只回滚到保存点,第一条日志仍然保留
  • 这允许在事务内部进行部分回滚,而不是全部回滚
  • 保存点适合复杂业务流程中的错误处理

场景四:自动提交模式

-- 查看当前自动提交模式
SELECT @@autocommit;

当前状态:

@@autocommit
1

结果解读:

  • @@autocommit = 1 表示开启自动提交(MySQL 默认)
  • 每条 SQL 语句自动作为一个事务提交
-- 关闭自动提交
SET autocommit = 0;

SELECT @@autocommit;

操作后的状态:

@@autocommit
0
-- 此时每条 SQL 都在事务中,需要手动 COMMIT
UPDATE employees SET score = score + 10 WHERE emp_id = 1;

-- 查看数据(在当前会话中已修改)
SELECT * FROM employees WHERE emp_id = 1;

当前会话中的数据:

emp_idemp_namedeptscore
1大翔技术部60
-- 如果不 COMMIT,其他会话看不到这个修改
-- 现在执行提交
COMMIT;

-- 恢复自动提交模式
SET autocommit = 1;

结果解读:

  • autocommit = 0 时,每个 SQL 都在一个隐式事务中,需要手动 COMMIT
  • 显式 START TRANSACTION 会临时覆盖 autocommit 的设置
  • 事务结束后(COMMIT/ROLLBACK),autocommit 恢复为原来的值

场景五:DDL 隐式提交

-- 查看初始状态
SELECT * FROM employees;

操作前的数据状态:

emp_idemp_namedeptscore
1大翔技术部60
2白歌技术部50
START TRANSACTION;

-- 执行 DML 操作
UPDATE employees SET score = score + 50 WHERE emp_id = 1;

-- 查看当前状态
SELECT * FROM employees WHERE emp_id = 1;

事务中的状态:

emp_idemp_namedeptscore
1大翔技术部110
-- 执行 DDL 操作(会触发隐式提交)
CREATE TABLE temp_table (id INT);

-- DDL 导致之前的 UPDATE 被自动提交!
-- 此时事务已经结束,ROLLBACK 无法回滚 UPDATE

ROLLBACK;

-- 查看最终状态(UPDATE 没有被回滚)
SELECT * FROM employees WHERE emp_id = 1;

操作后的数据状态:

emp_idemp_namedeptscore
1大翔技术部110

结果解读:

  • DDL(CREATE、ALTER、DROP 等)会触发隐式提交,导致当前事务被提交
  • ROLLBACK 执行时事务已经结束,所以 UPDATE 没有被回滚
  • 生产环境中应避免在事务中混用 DDL 和 DML

常见误区

误区正解
事务越大越好大事务持有锁时间长,影响并发。应在保证原子性的前提下尽量缩小事务范围。
ROLLBACK 可以回滚 DDL不可以。DDL(如 DROP TABLE)会隐式提交,无法回滚。
所有存储引擎都支持事务只有 InnoDB 支持事务。MyISAM 不支持。

面试考点

Q:什么是 ACID?

原子性(Atomicity):事务要么全成功,要么全失败;一致性(Consistency):事务前后数据一致;隔离性(Isolation):并发事务互不干扰;持久性(Durability):提交后数据永久保存。

Q:MySQL 默认的自动提交模式是什么?

autocommit = 1,即每条 SQL 语句自动作为一个独立事务提交。显式 START TRANSACTION 会临时禁用自动提交,直到 COMMIT 或 ROLLBACK。

Q:事务中可以使用 DDL 吗?

可以,但 DDL(CREATE、ALTER、DROP 等)会触发隐式提交,导致当前事务被提交,之前的操作无法回滚。

小结

  • 事务是数据库保证数据一致性的核心机制
  • ACID:原子性、一致性、隔离性、持久性
  • START TRANSACTION 开始事务,COMMIT 提交,ROLLBACK 回滚
  • 默认 autocommit = 1,显式事务会临时覆盖
  • DDL 会隐式提交,不可回滚

下一章引子:事务的隔离性通过隔离级别实现,不同隔离级别在并发性能和数据一致性之间做不同的权衡。

下一页
事务隔离级别