MySQL事务是数据库操作的一个重要概念,事务是指一组操作要么全部完成,要么全部不完成,是数据库的一个逻辑工作单元。事务的主要目的是确保数据库的一致性和可靠性。
- 事务是一组SQL语句的执行,要么全部成功,要么全部失败,不能出现部分成功,部分失败的结果。保证事务执行的原子操作。
- 事务的所有SQL语句全部执行成功,才能提交(commit)事务,把结果写回磁盘上。
- 事务执行过程中,有的SQL出现错误,那么事务必须要回滚(rollback)到最初的状态。
1. 事务的特性(ACID)
事务具有四个主要特性,通常被称为ACID特性:
- 原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不完成。事务在执行过程中发生错误会回滚到事务开始前的状态,就像这个事务从未执行过一样。
- 一致性(Consistency):事务执行前后,数据库必须保持一致性。比如,事务的执行不能违反数据库的完整性约束。
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。不同的隔离级别可以控制事务的可见性。
- 持久性(Durability):事务一旦提交,其结果就永久保存到数据库中,即使系统发生故障也不会丢失。
2. MySQL 事务控制语句
MySQL 提供了一组事务控制语句来管理事务:
- START TRANSACTION:显式地开启一个事务。
- COMMIT:提交事务,将事务中的所有操作保存到数据库中。
- ROLLBACK:回滚事务,撤销事务中的所有操作。
- SAVEPOINT:设置保存点,可以回滚到特定的保存点,而不是回滚整个事务。
- RELEASE SAVEPOINT:删除一个保存点。
- SET TRANSACTION:设置事务的隔离级别。
3. 事务的隔离级别
MySQL 提供了四种事务隔离级别,可以通过 SET TRANSACTION ISOLATION LEVEL
语句来设置:
- READ UNCOMMITTED:最低的隔离级别,一个事务可以读取未提交的其他事务的数据。这会导致脏读(Dirty Read)问题。
- READ COMMITTED:一个事务只能读取已经提交的事务的数据,避免了脏读问题,但可能会出现不可重复读(Non-repeatable Read)问题。
- REPEATABLE READ:默认的隔离级别,一个事务在开始时看到的数据是一致的,即使其他事务进行了更新,也不会看到变化,避免了不可重复读问题,但可能会出现幻读(Phantom Read)问题。
- SERIALIZABLE:最高的隔离级别,通过强制事务顺序执行,避免了所有并发问题,但性能会受到影响。
1. 脏读(Dirty Read)
脏读是指一个事务读取了另一个事务尚未提交的数据。这种情况可能导致读取到的数据是临时的或错误的,因为读取的事务无法保证这些数据最终会被提交。
示例:
- 事务A更新了一条记录的值,但尚未提交。
- 事务B读取了事务A更新后的值。
- 事务A回滚,撤销了更新。
- 事务B读取到的数据是不存在的临时数据,这就是脏读。
解决方法:通过设置隔离级别为 READ COMMITTED
或更高,可以避免脏读。
2. 不可重复读(Non-repeatable Read)
不可重复读是指在一个事务中两次读取同一条记录却得到了不同的结果。这通常是因为在两次读取之间,另一个事务修改并提交了这条记录。
示例:
- 事务A读取了一条记录的值。
- 事务B更新并提交了这条记录的值。
- 事务A再次读取同一条记录,得到了与第一次读取不同的值。
解决方法:通过设置隔离级别为 REPEATABLE READ
或更高,可以避免不可重复读。
3. 幻读(Phantom Read)
幻读是指在一个事务中两次查询同一条件的数据集,却得到了不同的数据集结果。这通常是因为在两次查询之间,另一个事务插入或删除了符合条件的记录。
示例:
- 事务A执行一条查询语句,查找符合某个条件的所有记录。
- 事务B插入了一条新记录,该记录符合事务A的查询条件,并提交。
- 事务A再次执行相同的查询,结果集包含了事务B插入的那条新记录。
解决方法:通过设置隔离级别为 SERIALIZABLE
可以避免幻读。
隔离级别和并发问题
为了更好地理解如何通过隔离级别避免这些并发问题,下面是各隔离级别对脏读、不可重复读和幻读的影响:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED | 允许 | 允许 | 允许 |
READ COMMITTED | 不允许 | 允许 | 允许 |
REPEATABLE READ | 不允许 | 不允许 | 允许 |
SERIALIZABLE | 不允许 | 不允许 | 不允许 |
通过选择适当的隔离级别,可以在性能和数据一致性之间取得平衡。例如,在高并发的环境中,为了提高性能,可能选择较低的隔离级别(如 READ COMMITTED
),而在需要严格数据一致性的场景中,可能选择较高的隔离级别(如 SERIALIZABLE
)。
串行化:锁实现 给所有事务排序。并发的效率低,数据的安全性高。
未提交读:没有做任何的并发控制。并发的效率高,数据的安全性最低。
已提交读(oracle)和可重复读(mysql):结合了数据的安全性&一致性和并发效率,MVCC多版本并发控制机理实现
4. 事务的使用示例
-- 开始事务
START TRANSACTION;
-- 执行一些SQL操作
INSERT INTO accounts (account_id, balance) VALUES (1, 1000);
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
-- 提交事务
COMMIT;
在出现错误时可以回滚事务
-- 开始事务
START TRANSACTION;
-- 执行一些SQL操作
INSERT INTO accounts (account_id, balance) VALUES (1, 1000);
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
-- 假设这里发生了错误
-- ROLLBACK 事务
ROLLBACK;
5. 保存点的使用
保存点可以在事务中创建多个回滚点,以实现部分回滚:
-- 开始事务
START TRANSACTION;
-- 执行一些SQL操作
INSERT INTO accounts (account_id, balance) VALUES (1, 1000);
SAVEPOINT sp1;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
-- 创建另一个保存点
SAVEPOINT sp2;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
-- 回滚到第一个保存点
ROLLBACK TO sp1;
-- 提交事务
COMMIT;
6. 隔离级别的设置
可以在全局或会话级别设置隔离级别:
-- 设置全局隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 设置会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
7. 注意事项
- 死锁:当两个事务相互持有对方需要的资源时,会发生死锁。MySQL能够检测到死锁并自动回滚一个事务。
- 性能:较高的隔离级别会降低并发性能,因此在实际应用中需要根据需求选择合适的隔离级别。