MySQL事务是一组不可分割的数据库操作序列,这些操作要么全部执行成功,要么全部不执行,是数据库保证数据一致性的核心机制。事务通常对应一个完整的业务场景,比如转账操作,需要同时完成扣款和加款两个步骤,两个步骤必须作为一个整体执行,不能出现只执行其中一个的情况。

什么是MySQL事务
从定义上来说,MySQL事务是访问并更新数据库中各种数据项的一个程序执行单元。在MySQL中,只有使用了Innodb数据库引擎的数据库或表才支持事务,MyISAM引擎是不支持事务的。事务的处理可以用来维护数据库的完整性,保证成批的SQL语句要么全部执行,要么全部不执行。
我们可以通过简单的示例来理解事务的作用,比如用户A向用户B转账100元,这个操作需要执行两条SQL语句:
-- 用户A扣款100元 UPDATE account SET balance = balance - 100 WHERE user_id = 'A'; -- 用户B加款100元 UPDATE account SET balance = balance + 100 WHERE user_id = 'B';
如果没有事务,当第一条语句执行成功,第二条语句执行失败时,就会出现用户A的钱扣了但用户B没收到钱的问题,而使用事务就可以避免这种情况,保证两条语句要么都成功,要么都回滚到操作前的状态。
事务的四大特性(ACID)
事务的四大特性是ACID,分别是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),这四个特性是事务正常工作的基础。
1. 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部发生,要么全部不发生。就像上面的转账示例,两条更新语句要么都执行成功,要么都失败,不会出现只执行一半的情况。如果事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
2. 一致性(Consistency)
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态。也就是说,事务执行前后,数据库的完整性约束没有被破坏。比如转账前后,用户A和用户B的总余额是不变的,这就是一致性的体现。如果事务执行过程中破坏了数据的一致性,比如扣款后加款失败,事务会回滚,保证数据回到一致的状态。
3. 隔离性(Isolation)
隔离性是指当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。比如两个事务同时操作同一张表的同一条数据,一个事务的修改在提交之前,另一个事务是看不到的,这样可以避免并发操作带来的数据混乱问题。
4. 持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,即使数据库发生故障也不会丢失提交的事务操作。比如转账事务提交后,即使数据库服务器宕机重启,用户A和B的余额变化依然是生效的,不会因为宕机而丢失这次操作的结果。
MySQL事务的隔离级别
MySQL提供了四种事务隔离级别,不同的隔离级别可以解决不同的并发问题,隔离级别越高,数据的一致性越好,但并发性能会越低。四种隔离级别从低到高分别是:读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)、串行化(Serializable)。
不同隔离级别对应的并发问题
在并发事务操作中,会出现三种典型的问题:脏读、不可重复读、幻读,不同的隔离级别对这三种问题的解决情况不同,具体对应关系如下:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读未提交(Read Uncommitted) | 会出现 | 会出现 | 会出现 |
| 读已提交(Read Committed) | 不会出现 | 会出现 | 会出现 |
| 可重复读(Repeatable Read) | 不会出现 | 不会出现 | 会出现(Innodb通过间隙锁解决) |
| 串行化(Serializable) | 不会出现 | 不会出现 | 不会出现 |
各隔离级别详解
1. 读未提交(Read Uncommitted)
这是最低的隔离级别,一个事务可以读取到另一个事务未提交的数据。比如事务A修改了某条数据但还没提交,事务B就可以读取到这个修改后的数据,如果事务A之后回滚了,那么事务B读取到的数据就是无效的,也就是脏读。这种隔离级别几乎没有用到实际场景中,因为数据一致性太差。
2. 读已提交(Read Committed)
一个事务只能读取到另一个事务已经提交的数据,解决了脏读的问题。但是会出现不可重复读的问题,也就是同一个事务中,两次读取同一数据,得到的结果可能不一样,因为在这两次读取之间,可能有其他事务修改并提交了这条数据。Oracle数据库默认使用的就是这种隔离级别。
3. 可重复读(Repeatable Read)
这是MySQL Innodb引擎默认的隔离级别,保证了在同一个事务中,多次读取同一数据的结果是一致的,解决了不可重复读的问题。在标准的SQL隔离级别定义中,可重复读级别下依然会出现幻读,也就是同一个事务中,两次查询符合条件的记录,第二次查询会多出之前没有的记录,通常是其他事务插入了新数据导致的。但是MySQL的Innodb引擎通过间隙锁(Gap Lock)机制,在可重复读级别下也解决了幻读的问题。
4. 串行化(Serializable)
这是最高的隔离级别,事务串行执行,完全避免了脏读、不可重复读、幻读的问题,但是并发性能最差,因为所有事务都是依次执行的,同一时间只能有一个事务操作数据。只有在数据一致性要求极高、并发量极低的场景下才会使用这种隔离级别。
如何查看和设置事务隔离级别
我们可以通过SQL语句查看和设置MySQL的事务隔离级别,以下是常用的操作命令:
-- 查看当前会话的事务隔离级别 SELECT @@session.transaction_isolation; -- 查看全局的事务隔离级别 SELECT @@global.transaction_isolation; -- 设置当前会话的事务隔离级别为读已提交 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 设置全局的事务隔离级别为可重复读 SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
需要注意的是,设置全局隔离级别需要管理员权限,而且修改后只对新的连接生效,已经存在的连接还是使用之前的隔离级别。
事务的基本使用示例
在MySQL中,我们可以通过以下语句来使用事务:
-- 开启事务 START TRANSACTION; -- 或者也可以用 BEGIN; -- 执行一组SQL操作 UPDATE account SET balance = balance - 100 WHERE user_id = 'A'; UPDATE account SET balance = balance + 100 WHERE user_id = 'B'; -- 如果操作都成功,提交事务 COMMIT; -- 如果操作出现错误,回滚事务 ROLLBACK;
在实际开发中,我们可以把需要保证一致性的多个SQL操作放在事务中,根据操作的结果决定是提交还是回滚,从而保证数据的正确性。