数据库中的事务
1.基本概念
MySQL 中的事务(Transaction)是一组原子性的 SQL 操作,要么全部执行成功,要么全部失败回滚,保证数据库从一个一致状态转换到另一个一致状态。
简单来说,就是把一组sql语句打包成一个整体,要么全部成功,要么全部失败。
2.事务的 ACID 特性
事务的 ACID 特性 是数据库管理系统(DBMS)中事务处理的四个核心原则,确保事务的可靠性和数据的一致性。
它们分别是原子性(A – Atomicity)、一致性(C – Consistency)、隔离性(I – Isolation)、持久性(D – Durability)。
特性 | 描述 |
---|---|
原子性 | 事务中的所有操作要么全部成功,要么全部失败回滚。 |
一致性 | 事务执行前后,数据库必须保持逻辑一致(如约束、外键、触发器等)。 |
隔离性 | 多个并发事务的执行互不干扰,彼此隔离。 |
持久性 | 事务提交后,对数据库的修改是永久性的,即使系统崩溃也不丢失。 |
3.事物的隔离级别
MySQL 支持四种隔离级别(默认是 可重复读(REPEATABLE READ)):
隔离级别 | 解决的问题 | 可能的问题 | 并发性 | |
---|---|---|---|---|
读未提交 | READ UNCOMMITTED | 无 | 脏读、不可重复读、幻读 | 最高 |
读已提交 | READ COMMITTED | 脏读 | 不可重复读、幻读 | 次高 |
可重复读 | REPEATABLE READ | 脏读、不可重复读 | 幻读(InnoDB 通过间隙锁避免幻读) | 次低 |
串行化 | SERIALIZABLE | 所有问题 | 性能低(通过锁表实现) | 最低 |
查看和设置隔离级别:
-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 设置会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
4.不同隔离级别下可能出现的问题:
在数据库事务中,隔离级别(Isolation Level) 决定了事务之间的可见性和影响程度。不同的隔离级别可以防止不同的问题,但也会带来性能或并发性的权衡。以下是不同隔离级别下可能出现的问题:
(1)读未提交和脏读
-
脏读(Dirty Read):事务可以读取其他事务尚未提交的数据。
-
示例:事务A修改了某行数据但未提交,事务B读取到了这个未提交的修改。如果事务A回滚,事务B读到的就是无效数据。
-
读未提交的适用场景:
几乎不使用,除非对数据一致性要求极低且追求最高并发性能。
(2)读已提交和不可重复读
-
不可重复读(Non-Repeatable Read):同一事务内多次读取同一数据,结果可能不同(因为其他事务已提交修改)。
-
示例:事务A第一次读取某行数据,事务B修改并提交该行数据后,事务A再次读取时发现数据变了。
-
读已提交的适用场景:
适用于大多数OLTP(在线事务处理)系统,如PostgreSQL的默认隔离级别。
(3)可重复读和幻读
-
幻读(Phantom Read):同一事务内多次执行相同查询,返回的行数可能不同(其他事务插入或删除了数据)。
-
示例:事务A查询
age > 18
的用户,事务B插入一条age=20
的记录并提交,事务A再次查询时发现多了一条记录。
-
MySQL的优化:
InnoDB引擎在REPEATABLE READ
下使用间隙锁(Gap Lock) 防止幻读,因此MySQL的REPEATABLE READ
实际上可以避免幻读。
也可以说Innodb是使用了Next-Key Lock(临键锁) 来解决幻读的问题,因为间隙锁(Gap Lock)是临键锁(Next-Key Lock)的一部分。
可重复读的适用场景:
适用于需要事务内数据一致性的场景,如MySQL的默认隔离级别。
(4)串行化
隔离级别为串行化时,基本可以说是百分百安全,安全问题得到了大幅度解决,但是相应的也出现了一些问题:
-
性能下降:所有事务串行执行,导致并发性能极低。
-
锁争用严重:大量读写操作会互相阻塞。
串行化的适用场景:
仅适用于严格要求数据绝对一致且并发量低的场景,如金融交易系统。
(5)总结
问题总结
隔离级别 | 脏读 | 不可重复读 | 幻读 | 性能影响 |
---|---|---|---|---|
读未提交 Read Uncommitted | ❌ 可能 | ❌ 可能 | ❌ 可能 | ⚡ 最高 |
读已提交 Read Committed | ✅ 防止 | ❌ 可能 | ❌ 可能 | ⚡⚡ 高 |
可重复读 Repeatable Read | ✅ 防止 | ✅ 防止 | ❌ 可能(但MySQL用间隙锁避免) | ⚡⚡⚡ 中等 |
串行化 Serializable | ✅ 防止 | ✅ 防止 | ✅ 防止 | ⚡⚡⚡⚡ 最低 |
5.不可重复读和幻读的区别?
无论是不可重复读还是幻读,本质上都是两次查询的结果不一致,那么他们有什么区别呢?
不可重复读vs幻读:
对比项 | 不可重复读(Non-Repeatable Read) | 幻读(Phantom Read) |
---|---|---|
定义 | 同一事务内,多次读取同一行数据,结果不同(数据被其他事务修改)。 | 同一事务内,多次执行相同查询,返回的行数不同(其他事务插入或删除了数据)。 |
关键区别 | 针对已存在的数据行(值被修改)。 | 针对结果集的行数变化(新增或删除数据)。 |
隔离级别 | 发生在读已提交,可重复读可以避免 | 可重复读默认仍可能出现(但InnoDB通过Next-Key Lock避免)。 |
示例 | 事务A读取age=20 ,事务B将age 改为25 并提交,事务A再次读取发现值变了。 | 事务A查询age > 20 返回2条数据,事务B插入一条age=30 并提交,事务A再次查询返回3条数据。 |
InnoDB解决方案 | 使用行锁(Record Lock) 锁定已读取的行。 | 使用临键锁(Next-Key Lock) 锁定记录和间隙,防止插入新数据。 |
影响 | 数据值不一致,可能导致逻辑错误。 | 数据集合不一致,影响统计或范围查询结果。 |
总的来说, 不可重复读和幻读的区别主要是:
-
不可重复读:同一行数据被修改 → 值变化。发生在读已提交。
-
幻读:查询结果集的行数变化 → 新增或删除数据。发生在可重复读。
-
InnoDB的优化:在
REPEATABLE READ
下,通过Next-Key Lock同时解决两者。