MySQL作为广泛使用的开源关系型数据库管理系统,提供了四种标准的事务隔离级别,以满足不同应用场景的需求
本文将通过实例详细解析MySQL的四种事务隔离级别:读未提交(READ UNCOMMITTED)、读已提交(READ COMMITTED)、可重复读(REPEATABLE READ)和串行化(SERIALIZABLE),并通过实例展示它们在实际操作中的表现
一、事务隔离级别的基本概念 事务(Transaction)是数据库操作的基本单位,它具有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),通常简称为ACID特性
其中,隔离性确保了事务在执行过程中不受其他事务的干扰
事务隔离级别决定了事务之间的隔离程度,不同的隔离级别提供了不同程度的数据一致性和并发性能
MySQL中的四种隔离级别,从低到高分别是:读未提交、读已提交、可重复读和串行化
隔离级别越低,系统的并发性能通常越高,但数据一致性风险也越大
二、MySQL事务隔离级别的实例解析 1. 读未提交(READ UNCOMMITTED) 读未提交是最低的事务隔离级别
在这种级别下,一个事务可以读取另一个事务尚未提交的数据
这种隔离级别存在严重的数据一致性问题,例如脏读(Dirty Read),即一个事务读取到了另一个事务未提交的修改
实例说明: 假设有一个名为`t_test`的表,结构如下: sql CREATE TABLE`t_test`( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, PRIMARY KEY(`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 表中插入了一条数据: sql INSERT INTO`t_test` VALUES(1, John); 现在,我们设置两个事务,事务一和事务二,它们的隔离级别均为读未提交
事务一更新表中的数据,但尚未提交,事务二尝试读取该数据
sql -- 事务一 SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; START TRANSACTION; UPDATE t_test SET name=Tom WHERE id=1; -- 确认更新成功,但未提交 SELECTFROM t_test WHERE id=1; -- 事务二(在事务一未提交的情况下执行) SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; START TRANSACTION; SELECT - FROM t_test WHERE id=1; -- 此时可以读取到事务一未提交的数据 在上述实例中,事务二读取到了事务一尚未提交的数据,这就是脏读
脏读可能导致数据不一致,因为事务一的数据修改可能最终被回滚
2. 读已提交(READ COMMITTED) 读已提交是大多数数据库的默认隔离级别
在这种级别下,一个事务只能读取另一个事务已经提交的数据
它解决了脏读问题,但仍然存在不可重复读(Non-repeatable Read)问题,即在一个事务中多次读取同一数据时,可能会得到不同的结果,因为在两次读取之间,其他事务可能已经提交了对该数据的修改
实例说明: 继续使用`t_test`表
事务一更新表中的数据并提交,事务二在事务一提交前后分别读取数据
sql -- 事务一 SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; START TRANSACTION; UPDATE t_test SET name=Tom WHERE id=1; COMMIT; --提交事务 -- 事务二 SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; START TRANSACTION; SELECT - FROM t_test WHERE id=1; -- 在事务一提交前读取,得到旧数据 --暂停一段时间,等待事务一提交 SELECT SLEEP(5) FROM dual; SELECT - FROM t_test WHERE id=1; -- 在事务一提交后读取,得到新数据 在上述实例中,事务二在事务一提交前后读取到的数据不同,这就是不可重复读
读已提交隔离级别适用于大多数业务场景,因为它避免了脏读,但在需要数据一致性的场景中可能不适用
3. 可重复读(REPEATABLE READ) 可重复读是MySQL的默认隔离级别
在这种级别下,一个事务在执行过程中,多次读取同一数据时,得到的结果是相同的,即使其他事务在这期间提交了对该数据的修改
它解决了不可重复读问题,但在某些情况下,仍然可能出现幻读(Phantom Read)问题,即一个事务在执行过程中,两次查询同样的条件,却得到了不同数量的结果,因为在两次查询之间,其他事务插入了符合查询条件的新数据
实例说明: 使用一个新的表`orders`,结构如下: sql CREATE TABLE orders( id INT PRIMARY KEY, amount DECIMAL(10,2) ); INSERT INTO orders VALUES(1,100); 事务一在读取数据后,事务二插入新数据并提交,事务一再次查询数据
sql -- 事务一 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; START TRANSACTION; SELECT - FROM orders WHERE id>=1; -- 结果:(1,100) -- 事务二(在事务一未提交的情况下执行) SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; START TRANSACTION; INSERT INTO orders VALUES(2,200); COMMIT; --提交事务 -- 事务一(继续执行) SELECT - FROM orders WHERE id>=1; -- 结果仍为:(1,100)(避免不可重复读,但可能产生幻读) 在上述实例中,事务一在两次查询之间,事务二插入了新数据,但事务一并未读取到新数据,避免了不可重复读
然而,如果事务一在第二次查询时希望获取到所有id大于等于1的记录,它会发现缺少了事务二插入的记录,这就是幻读
MySQL通过多版本并发控制(MVCC)和间隙锁(Gap Lock)在一定程度上解决了幻读问题
在可重复读隔离级别下,事务内第一次读时生成一个包含当前活跃事务ID列表的Read View,后续读取操作均基于该Read View,从而保证了数据的一致性
4.串行化(SERIALIZABLE) 串行化是最高的隔离级别
在这种级别下,所有的事务都是串行执行的,即一个事务必须等待另一个事务完成后才能执行
这种隔离级别完全避免了脏读、不可重复读和幻读问题,保证了数据的绝对一致性
然而,由于事务之间的串行执行,系统的并发性能会大幅下降
实例说明: 继续使用`orders`表
事务一在执行查询操作时,事务二被阻塞,直到事务一提交后才能执行更新操作
sql -- 事务一 SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; START TRANSACTION; SELECT - FROM orders WHERE id=1; -- 加共享锁 -- 事务二(在事务一未提交的情况下执行) SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; START TRANSACTIO