事务的4种隔离级别
-
Read Uncommitted
是隔离级别最低的一种事务级别。
脏读(Dirty Read):在这种隔离级别下,一个事务会读到另一个事务更新后但未提交的数据,如果另一个事务回滚,那么当前事务读到的数据就是脏数据,这就是脏读(Dirty Read)。
举例:准备好
students
表的数据,该表仅一行记录:mysql> select * from students; +----+-------+ | id | name | +----+-------+ | 1 | Alice | +----+-------+ 1 row in set (0.00 sec)
然后,分别开启两个MySQL客户端连接,按顺序依次执行事务A和事务B:
时刻 事务A 事务B 1 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 2 BEGIN; BEGIN; 3 UPDATE students SET name = 'Bob' WHERE id = 1; 4 SELECT * FROM students WHERE id = 1; 5 ROLLBACK; 6 SELECT * FROM students WHERE id = 1; 7 COMMIT; 当事务A执行完第3步时,它更新了
id=1
的记录,但并未提交,而事务B在第4步读取到的数据就是未提交的数据。随后,事务A在第5步进行了回滚,事务B再次读取
id=1
的记录,发现和上一次读取到的数据不一致,这就是脏读。可见,在Read Uncommitted隔离级别下,一个事务可能读取到另一个事务更新但未提交的数据,这个数据有可能是脏数据。
-
Read Committed
不可重复读(Non Repeatable Read):在这种隔离级别下,一个事务可能会遇到不可重复读(Non Repeatable Read)的问题。即在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致。
举例:准备好
students
表的数据:mysql> select * from students; +----+-------+ | id | name | +----+-------+ | 1 | Alice | +----+-------+ 1 row in set (0.00 sec)
然后,分别开启两个MySQL客户端连接,按顺序依次执行事务A和事务B:
时刻 事务A 事务B 1 SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SET TRANSACTION ISOLATION LEVEL READ COMMITTED; 2 BEGIN; BEGIN; 3 SELECT * FROM students WHERE id = 1; -- Alice 4 UPDATE students SET name = 'Bob' WHERE id = 1; 5 COMMIT; 6 SELECT * FROM students WHERE id = 1; -- Bob 7 COMMIT; 当事务B第一次执行第3步的查询时,得到的结果是
Alice
,随后,由于事务A在第4步更新了这条记录并提交,所以,事务B在第6步再次执行同样的查询时,得到的结果就变成了Bob
,因此,在Read Committed隔离级别下,事务不可重复读同一条记录,因为很可能读到的结果不一致。 -
Repeatable Read
幻读(Phantom Read):在这种隔离级别下,一个事务可能会遇到幻读(Phantom Read)的问题。即在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。
举例:准备好
students
表的数据:mysql> select * from students; +----+-------+ | id | name | +----+-------+ | 1 | Alice | +----+-------+ 1 row in set (0.00 sec)
然后,分别开启两个MySQL客户端连接,按顺序依次执行事务A和事务B:
时刻 事务A 事务B 1 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; 2 BEGIN; BEGIN; 3 SELECT * FROM students WHERE id = 99; -- empty 4 INSERT INTO students (id, name) VALUES (99, 'Bob'); 5 COMMIT; 6 SELECT * FROM students WHERE id = 99; -- empty 7 UPDATE students SET name = 'Alice' WHERE id = 99; -- 1 row affected 8 SELECT * FROM students WHERE id = 99; -- Alice 9 COMMIT; 事务B在第3步第一次读取
id=99
的记录时,读到的记录为空,说明不存在id=99
的记录。随后,事务A在第4步插入了一条id=99
的记录并提交。事务B在第6步再次读取id=99
的记录时,读到的记录仍然为空,但是,事务B在第7步试图更新这条不存在的记录时,竟然成功了,并且,事务B在第8步再次读取id=99
的记录时,记录出现了。可见,幻读就是没有读到的记录,以为不存在,但其实是可以更新成功的,并且,更新成功后,再次读取,就出现了。
-
Serializable
是最严格的隔离级别。在Serializable隔离级别下,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。
虽然Serializable隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景,一般都不会使用Serializable隔离级别。
-
默认隔离级别
如果没有指定隔离级别,数据库就会使用默认的隔离级别。在MySQL中,如果使用InnoDB,默认的隔离级别是Repeatable Read。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Blazor Hybrid适配到HarmonyOS系统
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 分享4款.NET开源、免费、实用的商城系统
· 解决跨域问题的这6种方案,真香!
· 一套基于 Material Design 规范实现的 Blazor 和 Razor 通用组件库