mysql基础【事务】
1. 事务简介
事务 是一组操作的集合,它是一个不可分割的工作单位。事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
在实际应用中,例如银行转账场景:
- 用户张三向李四转账1000元,涉及两个关键步骤:
- 张三账户余额减少1000元
- 李四账户余额增加1000元
这两个操作必须同时完成,否则会导致数据不一致(如只扣款未入账)。
1.1 示例流程图说明:
如果在执行过程中出现异常(如网络中断、数据库错误等),我们可以进行事务回滚,恢复到事务开始前的状态,确保数据一致性。
1.2 注意事项:
默认情况下,MySQL 的事务是自动提交的。也就是说,当执行一条 DML语句时,MySQL 会立即隐式地提交事务。
2. 事务操作
事务操作是数据库中实现数据一致性和完整性的关键手段。通过显式控制事务的开启、提交和回滚,可以确保一组相关操作要么全部成功,要么全部失败。
2.1 基础语法
在 MySQL 中,事务操作主要包括以下几个核心命令:
2.1.1开启事务
START TRANSACTION;
-- 或
BEGIN;
使用
START TRANSACTION
或BEGIN
来标记事务的开始。之后的所有 SQL 操作都将属于当前事务,直到提交或回滚。
2.1.2提交事务
COMMIT;
将事务中的所有更改永久保存到数据库中。一旦提交,事务结束,修改不可撤销。
2.1.3回滚事务
ROLLBACK;
放弃事务中所有的更改,恢复到事务开始前的状态。通常用于出现异常时保证数据一致性。
2.1.4查看/设置事务提交方式(自动提交模式)
默认情况下,MySQL 是自动提交模式(autocommit = 1
),即每条 DML 语句执行后立即提交。
2.1.4.1查看当前自动提交状态:
SELECT @@autocommit;
2.1.4.2关闭自动提交(启用手动事务控制):
SET @@autocommit = 0;
设置为 0 后,需要手动使用
COMMIT
或ROLLBACK
来结束事务。
2.2 操作示例
示例场景:银行转账(张三 → 李四 转账 1000 元)
假设初始数据如下:
id | name | money |
---|---|---|
1 | 张三 | 2000 |
2 | 李四 | 2000 |
2.2.1正常流程(事务成功提交)
-- 1. 关闭自动提交
SET @@autocommit = 0;-- 2. 开启事务
START TRANSACTION;-- 3. 执行业务逻辑
SELECT * FROM account WHERE name='张三';
UPDATE account SET money = money - 1000 WHERE name = '张三';
UPDATE account SET money = money + 1000 WHERE name = '李四';-- 4. 提交事务
COMMIT;
执行流程如下:
结果:
- 张三余额变为 1000
- 李四余额变为 3000
- 数据永久保存
2.2.2异常流程(事务回滚)
-- 1. 关闭自动提交
SET @@autocommit = 0;-- 2. 开启事务
START TRANSACTION;-- 3. 执行部分操作
UPDATE account SET money = money - 1000 WHERE name = '张三';-- 4. 模拟异常(如账户余额不足、网络中断等)
-- 假设此处抛出异常,无法继续执行下一条语句-- 5. 回滚事务
ROLLBACK;
执行流程如下:
结果:
- 张三余额仍为 2000
- 李四余额未变化
- 事务被撤销,保持数据一致性
2.2.3两种常用事务处理方式
2.2.3.1方式一:使用 START TRANSACTION
显式控制
START TRANSACTION;
-- 多条 SQL 操作
UPDATE ...;
UPDATE ...;
COMMIT; -- 或 ROLLBACK;
2.2.3.2方式二:通过关闭自动提交 + 手动提交
SET @@autocommit = 0;
-- 多条 SQL 操作
UPDATE ...;
UPDATE ...;
COMMIT; -- 或 ROLLBACK;
⚠️ 注意:若忘记提交或回滚,可能导致事务长时间挂起,影响性能。
3. 事务四大特性
事务的四大特性,通常被称为 ACID 特性,是保证数据库系统在并发环境下数据一致性和可靠性的核心原则。这四个特性分别是:
3.1 原子性(Atomicity)
- 定义:事务是一个不可分割的最小操作单元,事务中的所有操作要么全部成功执行,要么全部不执行。
- 特点:如果事务中任意一个操作失败,整个事务将被回滚,恢复到事务开始前的状态。
- 示例:银行转账中,张三扣款和李四收款必须同时完成;若其中一步失败,则全部撤销。
类比:就像“原子”一样,不能拆分,要么整体存在,要么整体不存在。
3.2 一致性(Consistency)
- 定义:事务执行前后,数据库必须从一个合法的一致状态转换到另一个合法的一致状态。
- 特点:
- 数据库的完整性约束(如主键、外键、唯一性等)不会被破坏;
- 所有业务规则都必须满足;
- 转账前后总金额不变(守恒)。
- 示例:张三转出1000元,李四收到1000元,总余额仍为4000元,保持一致。
注意:一致性是通过原子性、隔离性和持久性共同保障的。
3.3 隔离性(Isolation)
- 定义:多个事务并发执行时,彼此之间互不干扰,每个事务都像是在独立环境中运行。
- 特点:
- 防止并发事务之间的数据冲突;
- 保证事务处理过程中的中间状态对其他事务不可见;
- 实现方式依赖于事务隔离级别
- 示例:A 正在查询账户余额,B 同时修改该账户余额,A 不应看到 B 的未提交数据。
作用:避免脏读、不可重复读、幻读等问题。
3.4 持久性(Durability)
- 定义:一旦事务提交成功,它对数据库的修改就是永久性的,即使系统崩溃也不会丢失。
- 实现机制:
- 数据写入日志(如 MySQL 的 redo log);
- 定期刷盘(fsync)确保数据落盘;
- 重启后能恢复已提交事务的结果。
- 示例:用户完成转账并提交后,即使服务器突然断电,再次启动后数据依然正确。
核心目标:防止因硬件故障或意外中断导致的数据丢失。
3.5 四大特性总结对比表
特性 | 英文缩写 | 核心含义 | 关键点 |
---|---|---|---|
原子性 | A | 要么全做,要么全不做 | 事务不可分割 |
一致性 | C | 数据始终合法 | 满足完整性约束 |
隔离性 | I | 并发无干扰 | 控制可见性 |
持久性 | D | 提交即永久 | 写入日志 + 刷盘 |
4. 并发事务问题
在多用户并发访问数据库的场景下,多个事务可能同时操作同一数据。虽然事务具有隔离性(Isolation),但如果不加以控制,仍可能出现以下三种典型的数据不一致问题:脏读、不可重复读、幻读。
这些问题是由于事务之间缺乏适当的隔离机制导致的,因此需要通过设置合适的事务隔离级别来避免
4.1 脏读(Dirty Read)
4.1.1 定义:
一个事务读取到了另一个事务尚未提交的数据。
4.1.2 危害:
如果未提交事务最终回滚,那么读取到的数据就是“无效”的,会导致业务逻辑错误。
4.1.3 示例:
时间 | 事务A | 事务B |
---|---|---|
T1 | SELECT id=1 → 查询到值为 100 | |
T2 | UPDATE id=1 SET money = 200 (未提交) | |
T3 | SELECT id=1 → 查询到值为 200(脏数据) | |
T4 | ROLLBACK (撤销更新) |
最终结果:事务A读到了一个不存在的值(200),因为事务B已回滚。
说明:
- 事务B修改了数据但未提交;
- 事务A读取了这个未提交的数据;
- 这种情况称为“脏读”。
4.2 不可重复读(Non-Repeatable Read)
4.2.1 定义:
在一个事务内,多次读取同一行记录,但每次读取的结果不一致。
4.2.2 原因:
另一个事务在该期间对该行数据进行了 UPDATE 或 DELETE 操作并提交。
4.2.3 示例:
时间 | 事务A | 事务B |
---|---|---|
T1 | SELECT id=1 → 返回 100 | |
T2 | UPDATE id=1 SET money = 200; COMMIT | |
T3 | SELECT id=1 → 返回 200(与之前不同) |
事务A两次查询同一条记录,结果不同,造成“不可重复读”。
说明:
- 事务A第一次查询时,id=1 的值是100;
- 事务B更新后提交;
- 事务A第二次查询发现值变为200;
- 这体现了“不可重复读”。
4.3 幻读(Phantom Read)
4.3.1 定义:
一个事务按照某个条件查询数据时,没有找到对应的数据行;但在插入或再次查询时,却发现该数据已经存在,好像出现了“幻影”。
4.3.2 原因:
另一个事务在该期间执行了 INSERT 操作,并提交。
4.3.3 示例:
时间 | 事务A | 事务B |
---|---|---|
T1 | SELECT id=1 → 无结果 | |
T2 | INSERT INTO user VALUES(1, '张三', 25); COMMIT | |
T3 | SELECT id=1 → 查到张三 |
事务A第一次查不到数据,第二次却查到了——仿佛“凭空出现”了一条记录,即“幻读”。
说明:
- 事务A先查询,未找到满足条件的数据;
- 事务B插入新数据并提交;
- 事务A再次查询时发现了这条“新出现”的数据;
- 这种现象称为“幻读”。
5. 事务隔离级别
为了防止并发事务中出现 脏读、不可重复读、幻读 等问题,数据库提供了不同的 事务隔离级别(Transaction Isolation Level)。通过设置合适的隔离级别,可以在数据安全性和系统性能之间取得平衡。
5.1 四种标准隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 | 安全性 | 性能 |
---|---|---|---|---|---|
Read Uncommitted | 允许 | 允许 | 允许 | 最低 | 最高 |
Read Committed | 禁止 | 允许 | 允许 | 较低 | 较高 |
Repeatable Read(默认) | 禁止 | 禁止 | 允许 | 较高 | 中等 |
Serializable | 禁止 | 禁止 | 禁止 | 最高 | 最低 |
MySQL 默认隔离级别为:Repeatable Read
5.2 各隔离级别详解
5.2.1. Read Uncommitted(读未提交)
- 允许一个事务读取另一个事务尚未提交的数据。
- 可能导致 脏读、不可重复读、幻读。
- 性能最高,但安全性最低,一般不推荐使用。
适用场景:对数据一致性要求极低的临时查询。
5.2.2 Read Committed(读已提交)
- 只能读取到其他事务已经提交的数据。
- 解决了 脏读,但仍可能发生 不可重复读 和 幻读。
- 是 Oracle 数据库的默认隔离级别。
示例:
- 事务A第一次读取某行数据;
- 事务B更新并提交;
- 事务A第二次读取时看到新值 → 发生“不可重复读”。
5.2.3 Repeatable Read(可重复读)——MySQL 默认
- 保证在一个事务内多次读取同一数据的结果一致。
- 解决了 脏读 和 不可重复读。
- 但仍然可能发生 幻读(MySQL 使用 Next-Key Locks 在一定程度上缓解幻读)。
MySQL 的
Repeatable Read
实现机制:
- 使用 MVCC(多版本并发控制) + 间隙锁(Gap Lock) 来防止幻读;
- 在大多数情况下可以避免幻读,但在某些复杂场景下仍可能触发。
示例:
- 事务A查询
id=1
得到值为100;- 事务B修改并提交;
- 事务A再次查询仍看到100 → 满足“可重复读”。
5.2.4 Serializable(串行化)
- 最高的隔离级别,所有事务串行执行,不允许并发。
- 完全避免了 脏读、不可重复读、幻读。
- 性能最差,容易产生死锁,仅用于极端敏感场景。
实现方式:加表级锁或行级锁,强制顺序执行。
5.2 查看与设置事务隔离级别
5.2.1 查看当前隔离级别
SELECT @@TRANSACTION_ISOLATION;
输出示例:
REPEATABLE-READ
5.2.2 设置隔离级别
-- 设置会话级别的隔离级别
SET {SESSION} TRANSACTION ISOLATION LEVEL {READ COMMITTED};-- 设置全局级别的隔离级别(影响所有新连接)
SET {GLOBAL} TRANSACTION ISOLATION LEVEL {REPEATABLE READ};
支持的值{}:
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE
全局与会话{}:
SESSION
仅影响当前会话;GLOBAL
影响所有后续连接;- 修改后需重启服务才能永久生效(除非写入配置文件)。