sqlbulkcopy 事务中查询_MySQL事务的高级

本文详细介绍了MySQL的四种事务隔离级别:READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, 和 SERIALIZABLE,并通过情景模拟展示了脏读、不可重复读和幻读的现象。MySQL默认的隔离级别是REPEATABLE READ,可避免脏读和不可重复读。SERIALIZABLE虽然能避免所有问题,但可能导致锁竞争和超时。在实际开发中,应根据需求合理选择事务隔离级别。" 132946785,19666751,Java模块化打包工具:提升开发与部署效率的新方案,"['Java开发', '编程语言', '软件构建', '模块化', '打包工具']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

事务的隔离级别:

数据库是多线程并发访问的,所以很容易出现多个线程同时开启事务的情况,这样就会出现脏读、重复读以及幻读的情况,为了避免这种情况的发生,就需要为事务设置隔离级别。在mysql中,事务有4种隔离级别。

READ UNCOMMITTED:

READ UNCOMMITTED(读未提交)是事务中最低的级别,该级别下的事务可以读取到另一个事务中未提交的数据,也被称之为脏读(Dirty Read),这是相当危险的。由于该级别较低,在实际开发中避免不了任何情况,所以一般很少使用。

READ COMMITTED:

大多数的数据库管理系统默认的默认隔离级别都是READ COMMITTED(读提交),比如Oracle,该级别下的事务只能读取其他事务已经提交的内容,可以避免脏读,但不能避免重复读和幻读的情况。重复读就是在事务内重复读取了别的线程已经提交的数据,但两次读取的结果不一致,原因是查询的过程中其他事务做了更新的操作。幻读是指在一个事务内两次查询中数据条数不一致,原因是查询的过程中其他的事务做了添加操作。这两种情况并不算错误,但有些情况是不符合实际需求的。

REPEATABLE READ:

REPEATABLE READ(可重复读)是mysql默认的事务隔离级别,它可以避免脏读、不可重复读的问题,确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。但理论上,该级别会出现幻读的情况,不过mysql的存储引擎通过多版本并发控制机制解决了该问题,因此,该级别是可以避免幻读的。

SERIALIZABLE:

SERIALIZABLE(可串行化)是事务的最高隔离级别,它会强制对事务进行排序,使之不会发生冲突,从而解决脏读、幻读、重复读的问题。实际上,就是在每个读的数据行上加锁。这个级别,可能导致大量的超时现象和锁竞争,实际应用中很少使用。

脏读:

所谓的脏读就是指一个事务读取了另外一个事务未提交的数据。

情景模拟:

现在a账户和b账户银行里面的钱如下:

a2dd59d7851e4d7543fb2cc022b1ffd4.png

a账户要给b账户转账100元购买商品,如果a账户开启了一个事务,执行了下面操作:

aeee998dd75af89822bb6f0f234b342e.png29791efd126cb22c905a63dd50d08c90.png

如果a账户先不提交事务,就通知b账户来查询,由于b的隔离级别较低,此时就会读到a事务中未提交的数据,发现a确实给自己转了100元,然后给a发货,等b发货成功以后,a就将事务回滚,此时,b就会受到损失,这就是脏读造成的。

为了演示上述情况,首先需要开启两个命令窗口(相当于开启两个进程),分别模拟a账户和b账户,然后登录到mysql数据库,并将操作的数据库切换为chapter06。

按下ctrl + shift+t再开启一个窗口:

37836d3e66675cb69cd75b89465207f4.png

设置b账户中事务的隔离级别:

前面我们讲过mysql的默认隔离级别是REPEATABLE READ(可重复读)该级别是可以避免脏读的,因此,需要将b账户中的事务隔离级别设置为READ UNCOMMITTED(读未提交)。

set session transaction isolation level read uncommitted;

fbad32afa4713cca7ccdcafc2d322957.png

session表示当前会话,transaction表示事务,isolation表示隔离,level表示级别,read uncommitted表示当前的隔离级别。查询当前事务的隔离级别:

d8d117848c1d2f20c6b2bcf391afca52.png

b账户再来查询:

800954872beb81c01ef4517a6127d913.png

上述结果表示a账户已经成功给b账户转账了100元钱了。那b账户的人就开始发货了。此时,a账户的人进行事务的回滚了,那b账户的人就损失了。

8f91d2f104b4e34ca70aa27fd0b24d40.png

a账户回滚了以后,b账户再来查询发现钱没有了,这个叫生气.......

03f7752375a246d18b2dc036b64e4323.png

出现这种情况的原因就是因为b账户的事务隔离级别较低,因此读取到了a账户中还没有提交的内容,出现了脏读的情况。

b账户修改事务隔离级别为READ COMMITTED(读提交)再来查看余额:

为了防止脏读情况的发生,可以将b账户中的事务隔离级别设置为READ COMMITTED(读提交),该级别可以避免脏读。

set session transaction isolation level read committed;

ee42f1bb4ad6a3c1aa3d0269625d2ecf.png

f80653421265fa83ea698619cf0f908a.png

a账户开启事务进行转账:

d39e77a1c37fe4fa516a1f9b0f96856d.png

此时,a账户通知b账户转账成功了,b账户就来查询余额。

e7a7d99b7cca26f353aadca2ba05cfe1.png

查询结果表示b账户并没有查询到a账户未提交的内容,因此可以说明read committed隔离级别可以避免脏读。最后的时候a账户进行事务的回滚。

c122e1c352f46631951188885970fe8a.png

不可重复读:

所谓的不可重复读(non-repeatable-read)是指事务中两次查询的结果不一致,原因是在查询的过程中其他事务做了更新的操作。比如:银行在做统计报表时,第一次查询a账户有1000元钱,第二次查询a账户有900元钱,原因是统计期间a账户取出了100元,这样就会导致多次统计报表的结果不一致。

不可重复读和脏读有点类似,但是脏读是读取前一个事务未提交的脏数据,不可重复读是在事务内重复读取了别的线程已提交的数据。

演示不可重复读:

b账户设置read committed(读提交)隔离级别:

set session transaction isolation level read committed;

ec4f7fe5bfd37ab7868fa7234580c756.png

b账户再来开启事务后再查询当前各账户的余额信息(一定要开启事务再查询)

ab6c88603d577c1c01966a08236b41f2.png

a账户只需要执行修改的操作,不需要保证同步性:

84bd90771b38d16f69e262125e9ec841.png

ec124b52a92e0b0d7d45c8d23d3ce6b5.png

当a账户中的更新操作执行成功后,再在b账户中再次查询各账户的余额:

6509970075f372263e00bf35fe141cea.png

对比b账户两次查询结果可以发现,两次查询结果是不一致的,实现上这种操作是没有错的,但是如果在银行统计报表时,这种情况是不符合需求的,因为我们并不希望在一个事务中看到的查询结果不一致,这就是不可重复读。最后,再将b账户的事务进行提交:

7b70cf43d100674b581821508a110280.png

再来设置b账户repeatable read(可重复读)的隔离级别:

为了防止重复读的情况出现,可以将该事务的隔离级别设置为repeatable read(可重复读):

set session transaction isolation level repeatable read;

2f3d97abcfb07c524b520a2032347e75.png

再来验证b账户是否出现不可重复读:

62fcb91008bb4db28ad92b488c017db7.png

在a账户中不开启事务,直接使用update语句执行更新操作:

ad3365861cb09093c433f5be38adac52.png

当a账户中的update语句执行成功后,b账户再在当前事务中,再次查询各账户的余额信息:

9d69c915c672980245f389d6d9498c11.png

对比两次查询结果可以发现,查询的结果是一致的,并没有出现不同的数据,因此,可以说明事务的隔离级别为repeatable read时,可以避免重复读的情况。最后,将b账户中的事务提交:

769a231100112d6767b938904c8df7e2.png

幻读:

幻读(PHANTOM READ)又被称为虚读,是指在一个事务内两次查询中数据条数不一致,幻读和不可重复读有些类似,同样是在两次查询过程中,不同的是,幻读是由于其他事务做了插入记录的操作,导致记录数有所增加。例如:银行在做统计报表时统计account表中所有用户的总额时,此时总共有三个账户总共金额有3000元,这时新增了一个账户,并且存入了1000元,这时银行再统计时发现账户的总金额变为4000元,造成了幻读的情况。

情景模拟:

先来设置b账户的隔离级别:

由于前面将事务的隔离级别设置为repeatable read(可重复读),这种隔离级别可以避免幻读的出现,因此需要将事务的隔离级别设置得更低。

set session transaction isolation level read committed;

8b0319ba1dee0ecf9af36b586afe1891.png

再在b账户中开启一个事务,然后在当前事务中查询账户的余额信息:

2af3aa4188c19b75b8c0cd1c6be75555.png

a账户在进行添加操作之前,先查询一下:

65820bfa00a7484483783abbbf119547.png

再对a账户进行添加操作,a账户不用开启事务,直接执行添加操作即可:

03bf7a55c345f306dcf7f93f82f3d9e5.pngb36e0a2ad33314dfdc80b1128c1f851e.png

当a账户添加记录成功后,再在b账户中再次查询账户的余额信息:

b081bf27835f56562fdb4fbac92e6104.png

通过对比b账户设置read committed隔离级别前后,发现第二次查询数据时比第一次查询时多了一条记录,这种情况并不是错误,但可能不符合实际需要。最后将b账户的事务进行提交:

3c6e7250d502fdf076b65cf3ffd5ae93.png

重新设置b账户的隔离级别,再来验证是否出现幻读:

为了防止出现幻读,可以将b账户的隔离级别设置为repeatable read:

set session transaction isolation level repeatable read;

4c6083b973024fc5034e5f2b2f3da4fb.png

这样就可以再来验证是否出现幻读。

在b账户中重新开启事务,并在当前事务中查询当前账户的余额信息:

aa892a1fb215595ffd2f725f4a894f0d.png

在对a账户进行添加操作之前,先来查询一下:

ffb11658175d8684d1c1ad927680decc.png

再对a账户进行添加操作,在a账户中不开启事务,直接执行添加操作:

e9df3f844c8bacc27be9139ee7256e82.png

当a账户的添加操作执行成功后,b账户再次查询:

570b5a7dfcc32fa6e5537199bfcfb8f0.png

对比b账户的两次查询结果可以看出,在同一个事务中两次的查询结果是一致的,并没有出现重复读取的情况,因此可以说明当前事务的隔离级别可以避免幻读,最后b账户还需要使用commit语句提交当前的事务:

f3fe6d96b568d3961229bdebe87476d6.png

可串行化:

可串行化(serializable)是事务的最高隔离级别,它在每个读的数据行上加上锁,使之不可能相互冲突,因此会导致大量的超时现象。

情景模拟:

设置b账户中事务的隔离级别:

set session transaction isolation level serializable;

c9bc05c113cf22e5379461fe92b6b7b2.png

首先在b账户中开启事务,再来查询:

ef17547f130aad934ab753911672a1fb.png

再在a账户中开启一个事务,并在该事务中执行插入操作:

6218f46ed41e28527518c918b69bff58.png

通过执行结果可以看出,当b账户正在事务中查询余额信息时,a账户中的操作是不能立即执行的。
现在b账户查询完余额信息了,此时对b账户立即提交当前事务:

91902c54759d0389a207fcf8f6f366fc.png

当b账户中的事务提交成功后,a账户中的添加操作才能执行成功:

e99672a6029c958a2976970861130307.png28fc0e513cb977fcfa92bac09654dcc2.png

如果中间的时间太长的话,就会出现超时的错误信息:

5bf7486df4c47817c42e7a887ae00a47.png

b账户再来进行查询的时候,也是不能查询出来的:

051612195788936bd8f323a25ed0767f.png

需要对a账户进行提交事务,b账户才可以查询出来:

66aefde4047943b319251c2e5e598e57.png

2d678f0b9ebd072649c4e1d1f1c24345.png

从上述的情况可以说明,如果一个事务使用了serializable(可串行化)隔离级别时,在这个事务没有被提交前,其他的线程只能等到当前操作完成后,才能进行操作,这样会非常耗时,而且会影响数据库的性能,通过情况下是不会使用这种隔离级别的。

总结:

21f8a03f9fe5c385e78c6377362d9e94.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值