81人参与 • 2026-09-29 • Mysql
这个特性是通过事务隔离级别来实现的,不同的隔离级别能够解决不同的并发事务中的问题,但同时也会在性能和一致性之间做出权衡。
隔离性的作用与级别
隔离性的主要作用是控制事务之间的可见性,即一个事务对其他事务的影响。sql标准定义了四个隔离级别:
下面对该问题进行实验:
读未提交(read uncommitted)
首先,查看数据库事务隔离级别,并将隔离级别设置为读未提交。
使用如下命令查看事务隔离级别。
select @@session.transaction_isolation;

查询到隔离级别为可重复读。
设置隔离级别为读未提交。
set session transaction isolation level read uncommitted;

然后开启两个会话,每个会话中开启一个事务,模拟两个事务并发读取修改的情况。观察脏读情况。
首先,数据库中有两条数据。

会话一:
start transaction; -- a语句 select a.id,a.username from appointment a where a.id = 1; -- b语句 update appointment a set username = 'nihao12' where a.id = 1; -- c语句 select a.id,a.username from appointment a where a.id = 1; commit;
会话二:
start transaction; -- a语句 select a.id,a.username from appointment a where a.id = 1; commit;
首先,会话一开启事务并执行a语句,会话二开启事务并执行a语句。
结果如下:
会话一:

会话二:

接下来,会话一执行b语句,执行完后执行c语句观察结果。会话二在会话一执行完b语句后再次执行a语句。结果如下:
会话一:

会话二:

我们发现,会话二中读取到了会话一中未提交的数据。发生了脏读。
读已提交
根据读未提交中的内容将隔离级别设置为读已提交。

然后开启两个会话,每个会话中开启一个事务,模拟两个事务并发读取修改的情况。观察脏读情况。
首先,数据库中有两条数据。

会话一:
start transaction; -- a语句 select a.id,a.username from appointment a where a.id = 1; -- b语句 update appointment a set username = 'nihao12' where a.id = 1; -- c语句 select a.id,a.username from appointment a where a.id = 1; commit;
会话二:
start transaction; -- a语句 select a.id,a.username from appointment a where a.id = 1; commit;
首先,会话一开启事务并执行a语句,会话二开启事务并执行a语句。
结果如下:
会话一:

会话二:

接下来,会话一执行b语句,执行完后执行c语句观察结果。会话二在会话一执行完b语句后再次执行a语句。结果如下:
会话一:

会话二:

我们发现,会话二中没有读取到会话一中未提交的数据。
会话一提交数据,会话二继续执行a语句读取数据。结果如下:
会话一:

会话二:

结果表明,会话二读取到了会话一提交的数据。
可重复读
对于可重复读,和上面的步骤一样,我们从开启事务开始看起。
开启事务后,会话一执行a语句,会话二执行a语句。结果如下:
会话一:

会话二:

会话一执行b语句,然后执行c语句。会话二执行a语句。结果如下:
会话一:

会话二:

会话二无法读取到会话一未提交的数据。
会话一提交,会话二继续执行a语句。结果如下:
会话一:

会话二:

结果表明会话二无法读取到会话一提交的数据。会话二中数据可重复读取。
接下来将会话一,会话二中的代码改为下面的情况:
会话一:
start transaction; -- a语句 select * from appointment a where a.id<10; -- b语句 insert into appointment(id,username,id_card,department,date,time) values(3,'nihao5',1234,'uuuu1','2010/09/08','1231'); -- c语句 select * from appointment a where a.id<10; commit;
会话二:
start transaction; -- a语句 select * from appointment a where a.id<10; -- b语句 update appointment a set a.time = '1232' where a.id<10; -- c语句 select * from appointment a where a.id<10; commit;
步骤会话一会话二结果1start transactionstart transaction2执行a语句执行完会话一a语句执行会话二a语句内容相同3执行b语句4执行c语句执行完会话一c语句执行会话二a语句会话一中数据有两条,会话二中数据有两条(会话二a语句为快照读,读取不到会话一修改的内容。可重复读避免了读幻读。)5commit6执行a语句会话二中数据有2条7执行b语句更新结果为3条数据(写幻读)8执行c语句查出来了2条数据
我们发现在会话二中更新后发生了幻读的现象(快照读的情况下,会发生写幻读的情况)。
为了避免写幻读的情况。可以尝试加锁。将会话二的代码改为如下所示:
start transaction; -- a语句 select * from appointment a where a.id<10 for update; -- b语句 update appointment a set a.time = '1231' where a.id<10; -- c语句 select * from appointment a where a.id<10; commit;
上述代码中select … for update会加锁(具体加什么锁先不进行探讨)。执行后,会话一中的插入会被阻塞。
执行序列和结果如下表所示:
| 步骤 | 会话一 | 会话二 | 结果 |
|---|---|---|---|
| 1 | start transaction | start transaction | |
| 2 | 执行a语句 | 执行完会话一a语句执行会话二a语句 | 内容相同,会话二对内容加锁。 |
| 3 | 执行b语句 | 会话一插入语句被阻塞 | |
| 4 | 执行b语句更新 | 更新两条数据 | |
| 5 | 执行c语句 | 查出来两条数据 | |
| 6 | commit | 提交,锁释放 | |
| 7 | 再次执行b语句 | 插入成功 | |
| 8 | 执行c语句 | 三条数据 | |
| 9 | commit |
隔离级别是会话独立的:一个事务的隔离级别只决定它如何看到其他事务的数据,不会强制其他事务遵循自己的级别。
高隔离级别事务不会“限制”低隔离级别事务的行为:例如,即使事务 a 设置为 serializable,事务 b 仍可按 read uncommitted 运行,并读取 a 未提交的数据(如果 a 允许)。
实际影响体现在数据可见性上:低隔离级别的事务可能看到高隔离级别事务未提交的数据(脏读),或在高隔离事务中看到不一致快照。
注:对于以上实验,两个会话的隔离级别都是相同的。可以自己试下两个隔离级别不同的情况。
到此这篇关于mysql数据库事务的acid之隔离性实验的文章就介绍到这了,更多相关mysql事务acid之隔离性内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论