11.MySQL事务管理详解
MySQL事务管理详解
文章目录
MySQL事务管理
事务的概念
事务的版本支持
事务的提交方式
事务的相关演示
事务的隔离级别
查看与设置隔离级别
读未提交(Read Uncommitted)
读提交(Read Committed)
可重复读(Repeatable Read)
串行化(Serializable)
隔离级别总结
关于一致性
多版本并发控制
记录中的3个隐藏字段
undo日志
快照的概念
Read View
RR与RC的本质区别
MySQL事务管理
事务的概念
事务是数据库操作的基本单位,由一条或多条SQL语句组成。这些语句在逻辑上紧密关联,共同完成一个特定任务。比如银行转账场景:从A账户扣除金额、向B账户增加金额,这两个操作必须同时成功或失败,否则就会出现数据不一致的问题。
事务的核心特性是ACID:
- 原子性(Atomicity):事务内的操作要么全部完成,要么全部不完成。如果中间某个步骤失败,整个事务会回滚到初始状态。
- 一致性(Consistency):事务执行前后,数据库的完整性约束(如主键、外键)必须保持有效。
- 隔离性(Isolation):多个事务并发执行时,彼此之间互不干扰,避免数据混乱。
- 持久性(Durability):一旦事务提交,对数据的修改就是永久的,即使系统崩溃也不会丢失。
为什么需要事务?试想如果没有事务,当执行到转账的第二步时服务器突然宕机,A账户的钱被扣了但B账户没收到,这会导致严重的资金错误。事务的出现就是为了简化开发者的编程模型,让数据库自动处理这些复杂场景。
事务的版本支持
MySQL的事务支持依赖于存储引擎。通过SHOW ENGINES
命令可以看到不同引擎的特性:
mysql> SHOW ENGINES;
+--------------------+---------+--------------------------------------------------+--------------+------+------------+
| Engine | Support | Comment | Transactions | XA | Savepoints |
+--------------------+---------+--------------------------------------------------+--------------+------+------------+
| InnoDB | DEFAULT | Supports transactions, row-level locking, etc. | YES | YES | YES |
| MyISAM | YES | MyISAM storage engine | NO | NO | NO |
+--------------------+---------+--------------------------------------------------+--------------+------+------------+
关键字段解读:
- Transactions:是否支持事务。InnoDB支持,而MyISAM不支持。
- XA:是否支持分布式事务(跨多个数据库的事务)。
- Savepoints:是否支持保存点(事务内可回滚到某个中间状态)。
因此,如果需要事务功能,必须选择InnoDB引擎。这也是MySQL默认使用InnoDB的原因之一。
事务的提交方式
事务的提交分为两种模式:
- 自动提交(Auto-commit):每条SQL语句单独作为一个事务,执行完立即提交。
- 手动提交(Manual commit):通过
BEGIN
或START TRANSACTION
显式开启事务,直到执行COMMIT
或ROLLBACK
才会结束。
查看当前提交模式:
mysql> SHOW VARIABLES LIKE 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
切换提交模式:
SET autocommit = 0; -- 关闭自动提交(手动模式)
SET autocommit = 1; -- 开启自动提交(默认模式)
手动事务示例:
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- 或 ROLLBACK;
如果在执行过程中发生错误(如网络中断),未提交的事务会自动回滚,确保数据安全。
事务的相关演示
准备测试表
创建一个银行用户表:
CREATE TABLE bank_users (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(50),balance DECIMAL(10,2)
);
插入初始数据:
INSERT INTO bank_users (name, balance) VALUES ('张三', 1000);
演示一:事务的常规操作
-
开启事务:
START TRANSACTION;
-
插入数据:
INSERT INTO bank_users (name, balance) VALUES ('李四', 500);
-
查看未提交的数据:
在另一个终端执行:SELECT * FROM bank_users; -- 可能看不到李四的记录(取决于隔离级别)
-
回滚操作:
ROLLBACK;
-
验证数据回滚:
SELECT * FROM bank_users; -- 李四的记录消失
演示二:原子性
假设事务中执行两条SQL:
START TRANSACTION;
UPDATE bank_users SET balance = balance - 1000 WHERE id = 1; -- 超出余额
UPDATE bank_users SET balance = balance + 1000 WHERE id = 2;
COMMIT;
如果第一条SQL导致余额为负数(违反业务规则),整个事务会回滚,确保数据一致性。
演示三:持久性
提交后的事务即使遇到服务器宕机,数据也不会丢失。例如:
START TRANSACTION;
UPDATE bank_users SET balance = balance + 100 WHERE id = 1;
COMMIT; -- 数据持久化到磁盘
事务的隔离级别
四种隔离级别对比
隔离级别 | 脏读 | 不可重复读 | 幻读 | 加锁读 |
---|---|---|---|---|
读未提交(Read Uncommitted) | ✅ | ✅ | ✅ | ❌ |
读已提交(Read Committed) | ❌ | ✅ | ✅ | ❌ |
可重复读(Repeatable Read) | ❌ | ❌ | ❌ | ❌ |
串行化(Serializable) | ❌ | ❌ | ❌ | ✅ |
选择建议:
- 读未提交:几乎不用,数据混乱。
- 读已提交:Oracle默认级别,适合对一致性要求不高的场景。
- 可重复读:MySQL默认级别,平衡性能与一致性。
- 串行化:极端场景使用,性能差。
查看与设置隔离级别
查看全局/会话隔离级别:
SELECT @@global.tx_isolation; -- 全局
SELECT @@session.tx_isolation; -- 当前会话
设置隔离级别:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
多版本并发控制(MVCC)
记录中的3个隐藏字段
InnoDB的每条记录包含以下隐藏字段:
- DB_TRX_ID:6字节,最近修改该记录的事务ID。
- DB_ROW_ID:6字节,隐式自增主键。
- DB_ROLL_PTR:7字节,回滚指针,指向undo日志中的历史版本。
示例:插入一条记录后:
INSERT INTO students (name, age) VALUES ('王五', 20);
该记录的DB_TRX_ID
为当前事务ID,DB_ROLL_PTR
为NULL
(无历史版本)。
undo日志
undo日志用于事务回滚和MVCC。当事务修改数据时:
- 先将旧版本拷贝到undo log。
- 更新记录的
DB_TRX_ID
和DB_ROLL_PTR
。 - 提交后释放锁。
版本链形成:每次修改都会生成新版本,通过DB_ROLL_PTR
串联成链表。
Read View
Read View是事务进行快照读时生成的可见性视图,包含:
- m_ids:活跃事务ID列表。
- m_up_limit_id:最小活跃事务ID。
- m_low_limit_id:下一个待分配的事务ID。
- m_creator_trx_id:创建该View的事务ID。
可见性判断规则:
- 事务ID <
m_up_limit_id
:可见。 - 事务ID >=
m_low_limit_id
:不可见。 - 事务ID在两者之间且不在
m_ids
中:可见。
RR与RC的本质区别
- RR(可重复读):事务第一次快照读生成Read View,后续读复用该View,保证多次读取结果一致。
- RC(读已提交):每次快照读生成新的Read View,因此可能读到其他事务已提交的修改。
示例:
- 事务A修改数据并提交。
- 事务B在事务A提交前进行快照读,则RR下看不到修改,RC下能看到。
关于一致性
一致性是事务的最终目标,依赖原子性、隔离性和持久性共同保障。例如转账场景中:
- 原子性确保要么全部成功,要么全部失败。
- 隔离性防止其他事务看到中间状态(如A扣款后B未到账)。
- 持久性保证提交后数据永久保存。
如果业务逻辑存在漏洞(如未检查余额),一致性仍会被破坏,因此开发者需自行验证业务规则。
隔离级别总结
隔离级别 | 性能 | 安全性 | 适用场景 |
---|---|---|---|
读未提交 | 高 | 低 | 无需准确性,如统计分析 |
读已提交 | 中高 | 中 | 一般业务,如订单查询 |
可重复读 | 中 | 高 | 核心业务,如银行交易 |
串行化 | 低 | 最高 | 极端场景,如库存扣减 |
选择时需权衡性能与安全性,大多数场景推荐使用可重复读。