我的文章合集:https://gitee.com/mydb/interviewMySQL中事务有四种隔离级别:READUNCOMMITTEDREADCOMMITTEDREPEATABLEREAD)序列化(SERIALIZABLE)MySQL默认的事务隔离级别是可重复读(REPEATABLEREAD),这四种隔离级别的描述如下。1.READUNCOMMITTED读未提交,又称未提交读,处于该隔离级别的事务可以看到其他事务中未提交的数据。这个隔离级别可以读取到其他事务中未提交的数据,而未提交的数据可能会被回滚,所以我们称这个级别读取的数据为脏数据,这种问题称为脏读。2.READCOMMITTED读已经提交,也叫提交读,这个隔离级别的事务可以读取提交事务的数据,所以不会有脏读的问题。但是,由于在事务执行过程中可以读取其他事务提交的结果,所以同一个SQL查询在不同的时间可能会得到不同的结果。这种现象称为不可重复读。3、REPEATABLEREAD是MySQL默认的事务隔离级别,可以保证同一个事务多次查询的结果是一致的。但也会出现新的问题。比如这个级别的事务正在执行的时候,另外一个事务成功插入了一条数据,但是因为每次查询的结果都是一样的,所以不会查询到这条数据,并且在重复插入时失败(由于唯一约束)。很显然,这个信息在事务中是查询不到的,但是它自己是不能插入的。这称为幻读(PhantomRead)。4、SERIALIZABLE序列化,事务的最高隔离级别,它会强制事务进行排序,这样就不会发生冲突,从而解决脏读、不可重复读、幻读等问题,但是由于执行效率低,实际使用场景并不多。简单概括一下,MySQL对应脏读、不可重复读、幻读的四种事务隔离级别的关系如下:事务隔离级别脏读不可重复读幻读读未提交(READUNCOMMITTED)√√√读已提交READCOMMITTED)×√√可重复读(REPEATABLEREAD)××√序列化(SERIALIZABLE)×××光看上面的概念会比较抽象。下面我们通过执行差异的结果一步步了解这些隔离级别。前置知识1.事务相关的常用命令#查看MySQL版本selectversion();#打开事务starttransaction;#提交事务commit;#回滚事务rollback;2、MySQL8之前查询事务的隔离级别查看全局MySQL事务隔离级别的SQL和当前会话的事务隔离级别如下:select@@global.tx_isolation,@@tx_isolation;以上SQL执行结果如下图所示:3、MySQL8后查询事务的隔离级别select@@global.transaction_isolation,@@transaction_isolation;4.查看已连接客户端的详细信息。每个MySQL命令行窗口都是一个MySQL客户端。每个客户端可以独立设置(不同的)事务隔离级别,这也是MySQL并发事务演示的基础。下面是查询客户端连接的SQL命令:showprocesslist;上面的SQL执行结果如下:5.查询连接的客户端数量,可以使用如下SQL命令查询当前连接MySQL服务器的客户端数量:showstatuslike'Threads%';执行结果上述SQL的具体内容如下:6.设置客户端的事务隔离级别当前客户端的事务隔离级别可以通过如下SQL设置:setsessiontransactionisolationleveltransactionisolationlevel;事务隔离级别有4个值:READUNCOMMITTED、READCOMMITTED、REPEATABLEREAD、SERIALIZABLE。7、新建数据库和测试数据新建测试数据库和表信息,执行SQL如下:--createadatabasedropdatabaseifexiststestdb;createdatabasetestdb;usetestdb;--createatablecreatetableuserinfo(idintprimarykeyauto_increment,namevarchar(250)notnull,balancedecimal(10,2)notnulldefault0);--inserttestdatainsertintouserinfo(id,name,balance)values(1,'Java',100),(2,'MySQL',200);创建的表结构和数据如下:8.NameConvention接下来将通过两个窗口(两个client)演示脏读、不可重复读、幻读在不同事务隔离级别下的问题。左边黑色背景绿色文字的客户端,下面简称为“窗口1”,右边蓝色背景白色文字的客户端,下面简称为“窗口2”,如图下图:脏读一个事务读取另一个事务还没有提交的数据称为脏读。脏读demo的执行流程如下:执行步骤Client1(Window1)Client2(Window2)说明Step1:setsessiontransactionisolationlevelreaduncommitted;开始交易;从用户信息中选择*;将事务隔离级别设置为读取未提交;开始交易;查询用户列表,Java用户余额为100元。Step2开始交易;更新用户信息setbalance=balance+50wherename='Java';开始交易;Java用户账户充值50元;第3步从用户信息中选择*;查询用户列表,其中Java用户的余额变成了150元。脏读演示步骤1设置窗口2的事务隔离级别为readuncommitted。设置命令如下:setsessiontransactionisolationlevelreaduncommitted;PS:事务隔离级别readuncommitted存在脏读问题。然后使用命令查看当前连接窗口的事务隔离域,如下图:打开一个事务,查询用户列表信息,如下图:脏读演示Step2在window中打开一个事务1、加50元,但不提交交易,执行的SQL如下:脏读演示步骤3在窗口2再次查询用户列表,执行结果如下:从上面的结果可以可以看到window1中的事务还没有在window2中读取到Committed数据,这就是脏读。不可重复读不可重复读是指一个事务连续执行同一条SQL,但是两次读取的数据不同,即不可重复读。不可重复读demo的执行过程如下:执行步骤Client1(window1)Client2(window2)说明Step1setsessiontransactionisolationlevelreadcommitted;开始交易;从用户信息中选择*;设置事务隔离级别为Readthesubmitted;打开交易;查询用户列表,Java用户余额为100元。Step2开始交易;更新用户信息setbalance=balance+20wherename='Java';commit;开始交易;Java用户余额增加20元;提交事务。第3步从用户信息中选择*;查询用户列表,Java用户余额变为120元。窗口2中同一个事务的两次查询得到的结果不同,属于不可重复读。具体执行步骤如下。不可重复读演示步骤1设置窗口2的事务隔离级别为readcommitted,设置命令如下:setsessiontransactionisolationlevelreadcommitted;PS:readcommitted可以解决脏读问题,但是有一个不可重复读的问题。使用命令查看当前连接窗口的事务隔离域,如下图:在窗口2打开事务,查询用户表,执行结果如下:在查询列表中,余额Java用户是100元。不可重复读演示步骤2:在窗口1打开事务,给Java用户加20元,但不提交事务,然后在窗口2观察是否有脏读问题,具体执行结果为如下图所示:从上面的结果,我们可以看出,当窗口的事务隔离级别设置为readcommitted时,不存在脏读问题。接下来在窗口1提交事务,执行结果如下图:Non-repeatablereaddemoStep3切换到窗口2再次查询用户列表,执行结果如下:从上面结果,可以看到Java用户的余额变成了120元。在同一个事务中,如果连续两次查询的结果不一致,则为不可重复读。不可重复读和脏读的区别脏读可以读取其他事务中未提交的数据,而不可重复读读取的是其他事务已经提交的数据,但是两种读的结果不同。幻读幻读就像它的名字一样,它就像是一种幻觉。在一个事务中,明明找不到主键为X的数据,但是主键为X的数据就是无法插入,就像某种幻觉。幻读演示的执行过程如下:执行步骤client1(window1)client2(window2)说明step1setsessiontransactionisolationlevelrepeatableread;starttransaction;select*fromuserinfowhereid=3;settransaction隔离级别为可重复读;打开交易;查询3号用户的数据,查询结果为空。第2步开始交易;插入用户信息(id、名称、余额)值(3,'Spring',100);提交;开始交易;添加用户,用户号为3;提交交易。Step3insertintouserinfo(id,name,balance)values(3,'Spring',100);窗口2添加了3号用户的数据,但是执行失败。第4步select*fromuserinfowhereid=3;查询3号用户的数据,查询结果为空。具体执行结果见以下步骤。幻读演示步骤一:设置窗口2为可重复读,重复幻读问题,查询用户号3,执行SQL如下:setsessiontransactionisolationlevelrepeatableread;starttransaction;select*fromuserinfowhereid=3;上面的SQL执行结果如下图所示:从上面的结果我们可以看出,查询结果中id=3的数据为空。幻读演示步骤2:打开窗口1的事务,插入3号用户的数据,然后提交事务成功,执行SQL如下:starttransaction;insertintouserinfo(id,name,balance)values(3,'春天',100);提交;以上SQL执行结果如下图所示:幻读演示Step3在窗口2插入用户3号的数据,执行SQL如下:insertintouserinfo(id,name,balance)values(3,'弹簧',100);上述SQL执行结果如下图所示:添加用户数据失败,提示表中已经存在编号为3的数据,且该字段为主键,不能添加多个。幻读演示步骤4在窗口2,重新执行查询:select*fromuserinfowhereid=3;上面的SQL执行结果如下图所示:/在这个事务中,明明没有3号用户,但是在插入的时候却提示已经存在,这就是幻读。不可重复读和幻读的区别在两者的描述上有所不同。不可重复读描述的重点是修改操作,而幻读描述的重点是增删操作。总结本文演示了MySQL的4个事务隔离级别:读未提交(有脏读问题)、读已提交(有不可重复读问题)、可重复读(有幻读问题)和序列化,可以重复读是MySQL的默认事务隔离级别。脏读是指读取了其他事务未提交的数据,而不可重复读是指读取了其他事务提交的数据,但前后查询结果不同,而幻读是指无法执行查询,但无法插入。判断是非在己,名誉在人,得失在数。公众号:Java面试真题解析
