-
2021-01-27 03:19:42
mysql事务隔离级别与spring事务隔离级别的区别:
脏读:为什么会出现脏读,因为你对数据库的任何修改都会是立即生效的,至于别人能不能看到主要取决与你 是否加锁了,数据库的执行与事务没有关系,事务只是保证对数据库所做的操作会不会撤销而已,mysql默认是行级锁,修改时只会对修改的那几行加锁,加锁期间其他用户线程是看不到修改结果的,所以会导致不可重复读和幻读的问题;
mysql的事务隔离是通过行级锁实现的;
oracle的行级锁只有排他锁没有共享锁,默认不是使用锁机制来实现隔离的,而是通过版本控制,不会脏读但是会出现重复读问题,因为a线程在修改期间没有加锁,b线程仍然可以读取到
幻读的原因:
幻读的解释:幻读是因为a线程在一定范围内对一批数据进行修改,b线程新增了一条数据,导致用户以为自己没有全量修改;
幻读的原因:因为mysql默认是通过行级锁来实现的,而不是表级锁,那么在修改期间其他线程当然可以新插入数据了;如果使用了mysql表级锁的引擎如:MyIsAM,幻读就不可能出现;
spring的隔离级别:
spring的隔离级别一定是数据库锁机制支持的;如果数据库没有行级锁机制,也没有版本控制那么spring也没办法;
脏读和不可重复读的区别在于读的时候是否加了读共享锁,不可重复读加了读共享锁;脏读是写的时候加了排他锁
spring和数据库(mysql)隔离级别的比较:
spring管理的事务是逻辑事务,而物理事务和逻辑事务的区别在于 事务的传播行为,spring隔离级别和数据库是对应的,在做dml时,数据在数据库中其实已经修改了,只是没有生效而且由于多线程锁的关系,其他的线程过来是看不到该dml操作的结果的。
更多相关内容 -
MySQL数据库事务隔离级别详解
2020-12-16 04:53:45数据库事务隔离级别 数据库事务的隔离级别有4个,由低到高依次为 Read uncommitted:允许脏读。 Read committed: 防止脏读,最常用的隔离级别,并且是大多数数据库的默认隔离级别。 Repeatable read:可以防止脏... -
MySQL事务隔离级别
2019-03-21 01:54:28NULL 博文链接:https://cuishuangjia.iteye.com/blog/964885 -
Mysql事务隔离级别原理实例解析
2020-12-14 09:57:08再加上很多书都说可重复读解决了幻读问题,比如《mysql技术内幕–innodb存储引擎》等,不一一列举了,因此网上关于事务隔离级别的文章大多是有问题的,所以再开一文说明! 本文所讲大部分内容,皆有官网作为佐证,... -
MySQL数据库事务隔离级别
2021-02-26 04:08:52数据库隔离级别有四种,应用《高性能mysql》一书中的说明:然后说说修改事务隔离级别的方法:1.全局修改,修改mysql.ini配置文件,在最后加上#可选参数有:READ-UNCOMMITTED,READ-COMMITTED,REPEATABLE-READ,... -
MySQL 事务隔离级别
2021-09-07 08:35:33MySQL 中事务的隔离级别一共分为四种,分别如下: 序列化(SERIALIZABLE) 可重复读(REPEATABLE READ) 提交读(READ COMMITTED) 未提交读(READ UNCOMMITTED) 四种不同的隔离级别含义分别...1. 理论
MySQL 中事务的隔离级别一共分为四种,分别如下:
-
序列化(SERIALIZABLE)
-
可重复读(REPEATABLE READ)
-
提交读(READ COMMITTED)
-
未提交读(READ UNCOMMITTED)
四种不同的隔离级别含义分别如下:
-
SERIALIZABLE
❝
如果隔离级别为序列化,则用户之间通过一个接一个顺序地执行当前的事务,这种隔离级别提供了事务之间最大限度的隔离。
-
REPEATABLE READ
❝
在可重复读在这一隔离级别上,事务不会被看成是一个序列。不过,当前正在执行事务的变化仍然不能被外部看到,也就是说,如果用户在另外一个事务中执行同条 SELECT 语句数次,结果总是相同的。(因为正在执行的事务所产生的数据变化不能被外部看到)。
-
READ COMMITTED
❝
READ COMMITTED 隔离级别的安全性比 REPEATABLE READ 隔离级别的安全性要差。处于 READ COMMITTED 级别的事务可以看到其他事务对数据的修改。也就是说,在事务处理期间,如果其他事务修改了相应的表,那么同一个事务的多个 SELECT 语句可能返回不同的结果。
-
READ UNCOMMITTED
❝
READ UNCOMMITTED 提供了事务之间最小限度的隔离。除了容易产生虚幻的读操作和不能重复的读操作外,处于这个隔离级的事务可以读到其他事务还没有提交的数据,如果这个事务使用其他事务不提交的变化作为计算的基础,然后那些未提交的变化被它们的父事务撤销,这就导致了大量的数据变化。
在 MySQL 数据库种,默认的事务隔离级别是 REPEATABLE READ
2. SQL 实践
接下来通过几条简单的 SQL 向读者验证上面的理论。
2.1 查看隔离级别
通过如下 SQL 可以查看数据库实例默认的全局隔离级别和当前 session 的隔离级别:
MySQL8 之前使用如下命令查看 MySQL 隔离级别:
SELECT @@GLOBAL.tx_isolation, @@tx_isolation;
查询结果如图:
可以看到,默认的隔离级别为 REPEATABLE-READ,全局隔离级别和当前会话隔离级别皆是如此。
MySQL8 开始,通过如下命令查看 MySQL 默认隔离级别:
SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;
就是关键字变了,其他都一样。
通过如下命令可以修改隔离级别(建议开发者在修改时修改当前 session 隔离级别即可,不用修改全局的隔离级别):
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
上面这条 SQL 表示将当前 session 的数据库隔离级别设置为 READ UNCOMMITTED,设置成功后,再次查询隔离级别,发现当前 session 的隔离级别已经变了,如图1-2:
注意,如果只是修改了当前 session 的隔离级别,则换一个 session 之后,隔离级别又会恢复到默认的隔离级别,所以我们测试时,修改当前 session 的隔离级别即可。
2.2 READ UNCOMMITTED
2.2.1 准备测试数据
READ UNCOMMITTED 是最低隔离级别,这种隔离级别中存在脏读、不可重复读以及幻象读问题,所以这里我们先来看这个隔离级别,借此大家可以搞懂这三个问题到底是怎么回事。
下面分别予以介绍。
首先创建一个简单的表,预设两条数据,如下:
表的数据很简单,有 javaboy 和 itboyhub 两个用户,两个人的账户各有 1000 人民币。现在模拟这两个用户之间的一个转账操作。
注意,如果读者使用的是 Navicat 的话,不同的查询窗口就对应了不同的 session,如果读者使用了 SQLyog 的话,不同查询窗口对应同一个 session,因此如果使用 SQLyog,需要读者再开启一个新的连接,在新的连接中进行查询操作。
2.2.2 脏读
一个事务读到另外一个事务还没有提交的数据,称之为脏读。具体操作如下:
-
首先打开两个SQL操作窗口,假设分别为 A 和 B,在 A 窗口中输入如下几条 SQL (输入完成后不用执行):
START TRANSACTION; UPDATE account set balance=balance+100 where name='javaboy'; UPDATE account set balance=balance-100 where name='itboyhub'; COMMIT;
-
在 B 窗口执行如下 SQL,修改默认的事务隔离级别为 READ UNCOMMITTED,如下:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
-
接下来在 B 窗口中输入如下 SQL,输入完成后,首先执行第一行开启事务(注意只需要执行一行即可):
START TRANSACTION; SELECT * from account; COMMIT;
-
接下来执行 A 窗口中的前两条 SQL,即开启事务,给 javaboy 这个账户添加 100 元。
-
进入到 B 窗口,执行 B 窗口的第二条查询 SQL(SELECT * from user;),结果如下:
可以看到,A 窗口中的事务,虽然还未提交,但是 B 窗口中已经可以查询到数据的相关变化了。
这就是脏读问题。
2.2.3 不可重复读
不可重复读是指一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。具体操作步骤如下(操作之前先将两个账户的钱都恢复为1000):
-
首先打开两个查询窗口 A 和 B ,并且将 B 的数据库事务隔离级别设置为 READ UNCOMMITTED。具体 SQL 参考上文,这里不赘述。
-
在 B 窗口中输入如下 SQL,然后只执行前两条 SQL 开启事务并查询 javaboy 的账户:
START TRANSACTION; SELECT * from account where name='javaboy'; COMMIT;
前两条 SQL 执行结果如下:
-
在 A 窗口中执行如下 SQL,给 javaboy 这个账户添加 100 块钱,如下:
START TRANSACTION; UPDATE account set balance=balance+100 where name='javaboy'; COMMIT;
4.再次回到 B 窗口,执行 B 窗口的第二条 SQL 查看 javaboy 的账户,结果如下:
javaboy 的账户已经发生了变化,即前后两次查看 javaboy 账户,结果不一致,这就是不可重复读。
和脏读的区别在于,脏读是看到了其他事务未提交的数据,而不可重复读是看到了其他事务已经提交的数据(由于当前 SQL 也是在事务中,因此有可能并不想看到其他事务已经提交的数据)。
2.2.4 幻象读
幻象读和不可重复读非常像,看名字就是产生幻觉了。
我举一个简单例子。
在 A 窗口中输入如下 SQL:
START TRANSACTION; insert into account(name,balance) values('zhangsan',1000); COMMIT;
然后在 B 窗口输入如下 SQL:
START TRANSACTION; SELECT * from account; delete from account where name='zhangsan'; COMMIT;
我们执行步骤如下:
-
首先执行 B 窗口的前两行,开启一个事务,同时查询数据库中的数据,此时查询到的数据只有 javaboy 和 itboyhub。
-
执行 A 窗口的前两行,向数据库中添加一个名为 zhangsan 的用户,注意不用提交事务。
-
执行 B 窗口的第二行,由于脏读问题,此时可以查询到 zhangsan 这个用户。
-
执行 B 窗口的第三行,去删除 name 为 zhangsan 的记录,这个时候删除就会出问题,虽然在 B 窗口中可以查询到 zhangsan,但是这条记录还没有提交,是因为脏读的原因才看到了,所以是没法删除的。此时就产生了幻觉,明明有个 zhangsan,却无法删除。
这就是幻读。
看了上面的案例,大家应该明白了脏读、不可重复读以及幻读各自是什么含义了。
2.3 READ COMMITTED
和 READ UNCOMMITTED 相比,READ COMMITTED 主要解决了脏读的问题,对于不可重复读和幻象读则未解决。
将事务的隔离级别改为
READ COMMITTED
之后,重复上面关于脏读案例的测试,发现已经不存在脏读问题了;重复上面关于不可重复读案例的测试,发现不可重复读问题依然存在。上面那个案例不适用于幻读的测试,我们换一个幻读的测试案例。
还是两个窗口 A 和 B,将 B 窗口的隔离级别改为
READ COMMITTED
,然后在 A 窗口输入如下测试 SQL:
START TRANSACTION; insert into account(name,balance) values('zhangsan',1000); COMMIT;
在 B 窗口输入如下测试 SQL:
START TRANSACTION; SELECT * from account; insert into account(name,balance) values('zhangsan',1000); COMMIT;
测试方式如下:
-
首先执行 B 窗口的前两行 SQL,开启事务并查询数据,此时查到的只有 javaboy 和 itboyhub 两个用户。
-
执行 A 窗口的前两行 SQL,插入一条记录,但是并不提交事务。
-
执行 B 窗口的第二行 SQL,由于现在已经没有了脏读问题,所以此时查不到 A 窗口中添加的数据。
-
执行 B 窗口的第三行 SQL,由于 name 字段唯一,因此这里会无法插入。此时就产生幻觉了,明明没有 zhangsan 这个用户,却无法插入 zhangsan。
2.4 REPEATABLE READ
和 READ COMMITTED 相比,REPEATABLE READ 进一步解决了不可重复读的问题,但是幻象读则未解决。
REPEATABLE READ 中关于幻读的测试和上一小节基本一致,不同的是第二步中执行完插入 SQL 后记得提交事务。
由于 REPEATABLE READ 已经解决了不可重复读,因此第二步即使提交了事务,第三步也查不到已经提交的数据,第四步继续插入就会出错。
注意,REPEATABLE READ 也是 InnoDB 引擎的默认数据库事务隔离级别
2.5 SERIALIZABLE
SERIALIZABLE 提供了事务之间最大限度的隔离,在这种隔离级别中,事务一个接一个顺序的执行,不会发生脏读、不可重复读以及幻象读问题,最安全。
如果设置当前事务隔离级别为 SERIALIZABLE,那么此时开启其他事务时,就会阻塞,必须等当前事务提交了,其他事务才能开启成功,因此前面的脏读、不可重复读以及幻象读问题这里都不会发生。
3. 总结
总的来说,隔离级别和脏读、不可重复读以及幻象读的对应关系如下:
性能关系如图:
-
-
mysql事务隔离级别
2021-08-13 00:28:31mysql事务的隔离级别:定义了事务与事务之间的隔离程度; mysql有四种事务的隔离级别: 3. 由隔离性引发的问题 脏读、不可重复度、幻读 4. 演示四种隔离级别 注意:我的mysql是5.7.19,不同版本的命令会有些...1. 事务
2. 事务的隔离级别
- mysql事务的隔离级别:定义了事务与事务之间的隔离程度;
- mysql有四种事务的隔离级别:
3. 由隔离性引发的问题
- 脏读、不可重复度、幻读
4. 演示四种隔离级别
-
注意:我的mysql是5.7.19,不同版本的命令会有些微调
-
首先需要开启两个会话,才能演示不同事务之间的隔离性
-
分别查看两个会话中事务的隔离级别
select @@tx_isolation;
一般默认的都是【可重复读】隔离级别
-
把一个会话的事务隔离级别改变,依次演示各种问题
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED/*读未提交---隔离性低*/ READ COMMITTED/*读已提交---隔离性中*/ REPEATABLE READ/*可重复读---隔离性高*/ SERIALIZABLE/*可串行化---隔离性最高*/
-
现在:左边是【可重复读级别】、右边是【读未提交级别】
-
两个会话都开启事务,因为隔离性就是和事务有关的,离开事务不谈隔离级别
-
建立一张表
account(id,name,balance)
我们可以看到,在【读未提交】会话中建立一张表,在【可重复读】会话中是可以感知到这张表的创建的,隔离的仅仅是对表的dml操作(delete update insert),对表的创建是不隔离的; -
在左边插入数据,并且还没有提交事务,右边就可以查询到,我么就说右边的是【脏读】
-
我们可以做适当推理,在左边事务未提交时,右边都可以感知到,那么一旦左边操作提交,那么右边就一定是感应得到的,无论是删除、修改还是插入;这就说明,【脏读】是非常严重的问题,出现脏读就一定会出现【不可重复读】【幻读】,咱们来试试:
-
由此证明了【read uncommitted】 隔离级别会有脏读、不可重复读、幻读
-
接下来,把右边设置成【read committed】隔离级别,继续试验:
提醒: 隔离性的体现,一定要在事务中,因此start transaction;
是必不可少的;- 上述证明,【read committed】隔离级别没有脏读,但依然有不可重复读、幻读
-
接下来提交事务,双边均提交事务,把右边设置成【可重复读级别】:
上图证明了没有【脏读】的现象;
上图证明了没有【幻读】、【不可重复读】现象
-
需要强调的是,事务一旦提交,那么不同会话之间的数据便一致了,因为隔离性针对的是事务:
-
下面演示【可串行化】隔离级别
-
它与【可重复读】的主要区别是,可串行化会给表加锁,一旦一个事务加入可串行化序列,那么它可操作的表必须是空闲的表,若某个表正在被其他事务操作,那么可串行化事务便会一直等待,直到那个事务结束,或者查询超时:
可以观察到,右边会话的查询操作等待了很久,直到左边commit后,才有结果,串行化效率比较低,但适用于多个会话之间需要数据同步互通的系统,虽然需要等待一些时间,但实现了所有会话数据的一致性;
-
可串行化事务会出现两种情况:
1.A会话开启,事务开启并设置为可串行化,先操作某个表,事务仍在继续;此时另一个事务B,不管是什么隔离级别,再操作此表时,就只能selelct,不能进行dml操作
2.A会话开启,事务开启并设置为可串行化,但是事务B抢先操作了那张表,结果A就不能操作这张表了,连select语句都不行!这是不一样的两种情况,注意区分;
5. 隔离级别查询设置命令
-- 查询当前会话隔离级别 SELECT @@tx_isolation; -- 查询系统隔离级别(用户登陆时默认的隔离级别) SELECT @@global.tx_isolation; -- 设置当前会话隔离级别 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED/*读未提交---隔离性低*/ READ COMMITTED/*读已提交---隔离性中*/ REPEATABLE READ/*可重复读---隔离性高*/ SERIALIZABLE/*可串行化---隔离性最高*/ -- 设置系统当前系统隔离级别 SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED/*读未提交---隔离性低*/ READ COMMITTED/*读已提交---隔离性中*/ REPEATABLE READ/*可重复读---隔离性高*/ SERIALIZABLE/*可串行化---隔离性最高*/
-
MySQL事务隔离级别详解
2022-02-17 11:08:15目录 ...四、事务隔离的实现原理 1、隔离级别实现的原理 2、为什么建议尽量不要使用长事务? 3、如何避免长事务对业务的影响? 五、隔离级别引起的问题 1、脏读 2、不可重复读 3、幻读 4、目录
一、 4种隔离级别
InnoDB默认是可重复读的(REPEATABLE READ)。
1、读未提交(Read Uncommitted)
一个事务还没提交时,它做的变更就能被别的事务看到
允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
2、读提交(Read Committed)
一个事务提交之后,它做的变更才会被其他事务看到
只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
3、可重复读(Repeated Read)
一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读
4、串行读(Serializable)
串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后面访问的事务必须等前一个事务执行完成,才能继续执行。
完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
隔离级别
脏读(Dirty Read)
不可重复读(NonRepeatable Read)
幻读(Phantom Read)
读未提交(Read uncommitted)
可能
可能
可能
读提交(Read committed)
不可能
可能
可能
可重复读(Repeatable read)
不可能
不可能
可能
串行化(Serializable )
不可能
不可能
不可能
二、不同隔离级别的示例
mysql> create table T(c int) engine=InnoDB; insert into T(c) values(1);
我们来看看在不同的隔离级别下,事务 A 会有哪些不同的返回结果,也就是图里面 V1、V2、V3 的返回值分别是什么:
1)若隔离级别是“读未提交”, 则 V1 的值就是 2。这时候事务 B 虽然还没有提交,但是结果已经被 A 看到了。因此,V2、V3 也都是 2。
2)若隔离级别是“读提交”,则 V1 是 1,V2 的值是 2。事务 B 的更新在提交后才能被 A看到。所以, V3 的值也是 2。
3)若隔离级别是“可重复读”,则 V1、V2 是 1,V3 是 2。之所以 V2 还是 1,遵循的就是这个要求:事务在执行期间看到的数据前后必须是一致的。
4)若隔离级别是“串行化”,则在事务 B 执行“将 1 改成 2”的时候,会被锁住。直到事务 A 提交后,事务 B 才可以继续执行。所以从 A 的角度看, V1、V2 值是 1,V3 的值是 2。
在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。
1、在“可重复读”隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。 2、在“读提交”隔离级别下,这个视图是在每个 SQL 语句开始执行的时候创建的。 3、这里需要注意的是,“读未提交”隔离级别下直接返回记录上的最新值,没有视图概念; 4、而“串行化”隔离级别下直接用加锁的方式来避免并行访问。
总结来说,存在即合理,哪个隔离级别都有它自己的使用场景,你要根据自己的业务情况来定。我想你可能会问那什么时候需要“可重复读”的场景呢?我们来看一个数据校对逻辑的案例。
假设你在管理一个个人银行账户表。一个表存了每个月月底的余额,一个表存了账单明细。这时候你要做数据校对,也就是判断上个月的余额和当前余额的差额,是否与本月的账单明细一致。你一定希望在校对过程中,即使有用户发生了一笔新的交易,也不影响你的校对结果。这时候使用“可重复读”隔离级别就很方便。事务启动时的视图可以认为是静态的,不受其他事务更新的影响。
三、隔离级别的设置与查看
1、设置隔离级别
1)在my.ini中设置级别
vim /etc/my.ini transaction-isolation = {READ-UNCOMMITTED | READ-COMMITTED | REPEATABLE-READ | SERIALIZABLE}
2)使用set transaction 设置单个会话或者所有新进连接的隔离级别
SET [SESSION | GLOBAL] \ TRANSACTION ISOLATION LEVEL \ {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
(1)global,意思是此语句将应用于之后的所有session,而当前已经存在的session不受影响
(2)选择session,将应用于当前session内此点之后的所有事务(包括已开始但未提交的)。
(3)什么都不写,将应用于当前session内的下一个还未开始的事务。
2、查看当前隔离级别
SELECT @@global.tx_isolation; SELECT @@session.tx_isolation; SELECT @@tx_isolation; show variables like 'transaction_isolation'; +-----------------------+----------------+ | Variable_name | Value | +-----------------------+----------------+ | transaction_isolation | READ-COMMITTED | +-----------------------+----------------+
四、事务隔离的实现原理
1、隔离级别实现的原理
在 MySQL 中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上一次的最新值,通过回滚操作,都可以得到前一个状态的值。假设一个值从 1 被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录:
当前值是 4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的 read-view。
如图中看到的,在视图 A、B、C 里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。
对于 read-viewA,要得到 1,就必须将当前值依次执行图中所有的回滚操作得到。同时你会发现,即使现在有另外一个事务正在将 4 改成 5,这个事务跟 read-view A、B、C 对应的事务是不会冲突的。
你一定会问,回滚日志总不能一直保留吧,什么时候删除呢?答案是,在不需要的时候才删除。也就是说,系统会判断,当没有事务再需要用到这些回滚日志时,回滚日志会被删除。
什么时候才不需要了呢?就是当系统里没有比这个回滚日志更早的 read-view 的时候。
2、为什么建议尽量不要使用长事务?
长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。
在 MySQL 5.5 及以前的版本,回滚日志是跟数据字典一起放在 ibdata 文件里的,即使长事务最终提交,回滚段被清理,文件也不会变小。我见过数据只有 20GB,而回滚段有200GB 的库。最终只好为了清理回滚段,重建整个库。除了对回滚段的影响,长事务还占用锁资源,也可能拖垮整个库,这个我们会在后面讲锁的时候展开。
3、如何避免长事务对业务的影响?
1)从应用开发端来看:
(1)确认是否使用了 set autocommit=0。这个确认工作可以在测试环境中开展,把MySQL 的 general_log 开起来,然后随便跑一个业务逻辑,通过 general_log 的日志来确认。一般框架如果会设置这个值,也就会提供参数来控制行为,你的目标就是把它改成 1,否则事务一直等着被提交占用资源。
(2)确认是否有不必要的只读事务。有些框架会习惯不管什么语句先用 begin/commit 框起来。我见过有些是业务并没有这个需要,但是也把好几个 select 语句放到了事务中。这种只读事务可以去掉。
(3)业务连接数据库的时候,根据业务本身的预估,通过 SET MAX_EXECUTION_TIME 命令,来控制每个语句执行的最长时间,避免单个语句意外执行太长时间。
2)其次,从数据库端来看:
(1)监控 information_schema.Innodb_trx 表,设置长事务阈值,超过就报警 / 或者 kill;
(2)Percona 的 pt-kill 这个工具不错,推荐使用;
(3)在业务功能测试阶段要求输出所有的 general_log,分析日志行为提前发现问题;
(4)如果使用的是 MySQL 5.6 或者更新版本,把 innodb_undo_tablespaces 设置成2(或更大的值)。如果真的出现大事务导致回滚段过大,这样设置后清理起来更方便。
五、隔离级别引起的问题
1、脏读
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据(出现在读未提交隔离级别下)
############## session 1 ############## mysql> select @@session.tx_isolation; +-----------------------+ | @@session.tx_isolation | +-----------------------+ | REPEATABLE-READ | +-----------------------+ 1 row in set (0.00 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into ttd values(1); Query OK, 1 row affected (0.05 sec) mysql> select * from ttd; +------+ | id | +------+ | 1 | +------+ 1 row in set (0.00 sec) ############session 2(会话2):############## # 在REPEATABLE-READ 级别下 不会出现脏读 mysql> select * from ttd; Empty set (0.00 sec) #设置级别为读未提交 mysql> set session transaction isolation level read uncommitted; Query OK, 0 rows affected (0.00 sec) mysql> select @@session.tx_isolation; +------------------------+ | @@session.tx_isolation | +------------------------+ | READ-UNCOMMITTED | +------------------------+ 1 row in set (0.00 sec) #读到了session 1未提交,但修改的数据 mysql> select * from ttd; +------+ | id | +------+ | 1 | +------+ 1 row in set (0.00 sec)
结论:session 2 在READ-UNCOMMITTED 下读取到session 1 中未提交事务修改的数据.
2、不可重复读
是指在一个事务内(前提是在事务中,如果一个session是事务,另一个session不是事务则不影响),多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。
这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。(发生在读提交隔离级别下)
############session 1(会话1):############## #设置隔离级别为 读提交 mysql> select @@session.tx_isolation; +------------------------+ | @@session.tx_isolation | +------------------------+ | READ-COMMITTED | +------------------------+ 1 row in set (0.00 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from ttd; +------+ | id | +------+ | 1 | +------+ 1 row in set (0.00 sec) ############session 2(会话2):############## mysql> insert into ttd values(2); Query OK, 1 row affected (0.00 sec) mysql> select * from ttd; +------+ | id | +------+ | 1 | | 2 | +------+ 2 rows in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.02 sec) ############session 1(会话1):############## #和第一次的结果不一样,READ-COMMITTED 级别出现了不重复读 mysql> select * from ttd; +------+ | id | +------+ | 1 | | 2 | +------+ 2 rows in set (0.00 sec)
原因是因为session1 是 read committed (读提交)级别,在没有commit的情况下读到的两次数据不一致
可重复读级别就不会出现上述下现象
############session 1(会话1):############## mysql> select @@session.tx_isolation; +------------------------+ | @@session.tx_isolation | +------------------------+ | REPEATABLE-READ | +------------------------+ 1 row in set (0.00 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from ttd; +------+ | id | +------+ | 1 | | 2 | +------+ 2 rows in set (0.00 sec) ############session 2(会话2):############## mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into ttd values(3); Query OK, 1 row affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.03 sec) ############session 1(会话1):############## #和第一次的结果一样,REPEATABLE-READ级别出现了重复读,并没有读到3,直到session1 commit之后才能读到3 mysql> select * from ttd; +------+ | id | +------+ | 1 | -------- | 2 | +------+ 2 rows in set (0.00 sec)
3、幻读
第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
mysql>CREATE TABLE `demo` ( `id` bigint(20) NOT NULL default '0', `value` varchar(32) default NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB mysql> select @@global.tx_isolation, @@tx_isolation; +-----------------------+-----------------+ | @@global.tx_isolation | @@tx_isolation | +-----------------------+-----------------+ | REPEATABLE-READ | REPEATABLE-READ | +-----------------------+-----------------+
实验一:
Session A
Session B
START TRANSACTION;
START TRANSACTION;
SELECT * FROM demo;
empty set
INSERT INTO demo VALUES (1, 'a');
SELECT * FROM demo;
empty set
COMMIT;
SELECT * FROM demo;
empty set
INSERT INTO demo VALUES (1, 'a');
ERROR 1062 (23000):
Duplicate entry '1' for key 1
刚刚明明告诉我没有这条记录的,在REPEATABLE-READ(可重复读)级别下出现了幻读
实验二:
Session A
Session B
START TRANSACTION;
START TRANSACTION;
SELECT * FROM demo;
+------+-------+
| | id | value |
| +------+-------+
| | 1 | a |
| +------+-------+
INSERT INTO demo VALUES (2, 'b');
| SELECT * FROM demo;
| +------+-------+
| | id | value |
| +------+-------+
| | 1 | a |
| +------+-------+
COMMIT;
| SELECT * FROM demo;
| +------+-------+
| | id | value |
| +------+-------+
| | 1 | a |
| +------+-------+
| UPDATE t_bitfly SET value='z';
| Rows matched: 2 Changed: 2 Warnings: 0
| SELECT * FROM demo;
| +------+-------+
| | id | value |
| +------+-------+
| | 1 | z |
| | 2 | z |
| +------+-------+
| (怎么多出来一行)
本事务中第一次读取出一行,做了一次更新后,另一个事务里提交的数据就出现了。也可以看做是一种幻读。
当隔离级别是可重复读,且禁用innodb_locks_unsafe_for_binlog的情况下,在搜索和扫描index的时候使用的next-key locks可以避免幻读。
4、在可重复读级别出现前后数据不一致现象
Session A
Session B
START TRANSACTION;
START TRANSACTION;
| SELECT * FROM demo;
| +----+-------+
| | id | value |
| +----+-------+
| | 1 | a |
| +----+-------+
INSERT INTO demo VALUES (2, 'b');
COMMIT;
| SELECT * FROM demo;
| +----+-------+
| | id | value |
| +----+-------+
| | 1 | a |
| +----+-------+
| SELECT * FROM demo LOCK IN SHARE MODE;
| +----+-------+
| | id | value |
| +----+-------+
| | 1 | a |
| | 2 | b |
| +----+-------+
| SELECT * FROM demo FOR UPDATE;
| +----+-------+
| | id | value |
| +----+-------+
| | 1 | a |
| | 2 | b |
| +----+-------+
| SELECT * FROM demo;
| +----+-------+
| | id | value |
| +----+-------+
| | 1 | a |
| +----+-------+
如果使用普通的读,会得到一致性的结果,如果使用了加锁的读,就会读到“最新的”“提交”读的结果。
本身,可重复读和提交读是矛盾的。在同一个事务里,如果保证了可重复读,就会看不到其他事务的提交,违背了提交读;如果保证了提交读,就会导致前后两次读到的结果不一致,违背了可重复读。
六、总结
InnoDB提供了这样的机制,在默认的可重复读的隔离级别里,可以使用加锁读去查询最新的数据(提交读)。MySQL InnoDB的可重复读并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁读使用到的机制就是next-key locks。
四个级别逐渐增强,每个级别解决一个问题。事务级别越高,性能越差,大多数环境read committed 可以用
-
MySQL查看和修改事务隔离级别的实例讲解
2020-12-14 09:55:14查看事务隔离级别 在 MySQL 中,可以通过show variables like ‘%tx_isolation%’或select @@tx_isolation;语句来查看当前事务隔离级别。 查看当前事务隔离级别的 SQL 语句和运行结果如下: mysql> show variables ... -
五分钟搞清楚MySQL事务隔离级别
2021-01-06 22:44:40作者:伞U ...好久没碰数据库了,只是想起自己当时在搞数据库的...为了说明问题,我们打开两个控制台分别进行登录来模拟两个用户(暂且成为用户 A 和用户 B 吧),并设置当前 MySQL 会话的事务隔离级别。 一. read unco -
Mysql事务隔离级别之读提交详解
2020-12-15 21:31:27查看mysql 事务隔离级别 mysql> show variables like '%isolation%'; +---------------+----------------+ | Variable_name | Value | +---------------+----------------+ | tx_isolation | READ-COMMITTED | +---... -
Mysql事务隔离级别测试
2022-01-28 16:24:04以下几个概念是事务隔离级别要实际解决的问题,所以需要搞清楚都是什么意思。 脏读 脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据... -
深入理解mysql事务隔离级别和锁机制
2022-03-13 10:30:52其实本质上,出现出现这些问题的本质就是数据库的多事务并发问题,针对这些问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制机制等来解决并发问题。接下来,我会详细的介绍这些机制,让大家深入理解... -
浅析MySQL事务隔离级别对其性能的影响
2021-01-26 00:53:03浅析MySQL事务隔离级别对其性能的影响MySQL对事务的隔离级别共分为四个级别,分别是:1.READ UNCOMMITTED 读未提交2.READ COMMITTED读提交3.REPEATABLE可重读4.SERIALIABLE可串读MySQL默认工作在级别三下。... -
四个案例看懂 MySQL 事务隔离级别
2021-08-30 16:54:52很多小伙伴对 MySQL 的隔离级别一直心存疑惑,其实这个问题一点都不难,关键看怎么讲!单纯的看理论,绝对让你晕头转向,但是,如果我们通过几个实际的 SQL 来演示一些,大家就会发现这玩意原来这么简单! 今天松哥... -
MySQL 事务隔离级别和实现原理
2021-10-08 15:48:51一、MySQL 事务 本文所说的 MySQL 事务都是指在 InnoDB 引擎下,MyISAM 引擎是不支持事务的。 数据库事务指的是一组数据操作,事务内的操作要么全部成功,要么全部失败。什么都不做,不一定是真的什么都没... -
MySQL事务隔离级别的实验
2022-03-14 18:59:22MySQL的事务隔离级别一共有四个,分别是读未提交、读已提交、可重复读以及可串行化。 默认是什么? 它是可重复读。 在两个终端里,打开两个mysql客户端。数据初始时,id为1, age为11 终端1 终端2 结论 start... -
MySQL事务隔离级别以及MVCC一致性视图的实现原理
2021-07-20 11:31:29详细介绍MySQL数据库事务隔离级别的实现原理,以及MVCC一致性视图的概念和实现。 -
mysql事务隔离级别及读问题的介绍
2022-03-20 21:55:10事务的四大隔离级别 序列化(SERIALIZABLE):如果隔离级别为序列化,则用户之间通过一个接一个顺序地执行当前的事务,这种隔离级别提供了事务之间最大限度的隔离 ...mysql默认事务隔离级别 提交读(READ COMMITTE -
mysql事务隔离级别——可重复读
2021-11-24 00:07:37设置事务隔离级别为读取已提交| 事务A 事务B 开启事务,设置事务隔离级别为读取已提交 查到5条记录 开启事务,插入一条记录id=6 事务提交 继续查询,依然查到6条记录 这就出现了一个问题,... -
mysql事务隔离级别——读已提交
2021-11-24 01:29:18开启事务,设置事务隔离级别为读未提交 查到5条记录 开启事务,插入一条记录id=6 ,事务并未提交 继续查询,查到6条记录 事务回滚 继续查询,查到5条记录 这样在事务A中就出现了脏读数据 事务脏... -
mysql事务隔离级别及实现原理
2019-09-04 22:22:33近来需要学习的东西实在太多了,看了容易忘记。准备写个blog记录下,方便以后复习。本文主要是讲解mysql的事务隔离级别和不同级别所出现的问题。 -
MySQL事务隔离级别详解.docx
2022-01-08 16:37:17MySQL事务隔离级别详解.docx -
mysql 事务隔离级别.pdf
2021-08-26 15:13:00mysql 事务隔离级别.pdf