当前位置: 首页 > 科技观察

面试惊喜:说说MySQL的事务隔离级别?

时间:2023-03-22 14:06:48 科技观察

作者|雷哥来源|Java面试题解析(ID:aimianshi666)转载请联系授权(微信ID:GG_Stone)MySQL事务隔离级别是为了解决并发事务相互干扰的问题。MySQL总的事务隔离级别有以下4种:READUNCOMMITTED:读未提交。READCOMMITTED:读取已提交。REPEATABLEREAD:可重复读。SERIALIZABLE:序列化。一、四种事务隔离级别1.READUNCOMMITTED读未提交,也叫未提交读。此隔离级别的事务可以看到其他事务中未提交的数据。这个隔离级别可以读取到其他事务中未提交的数据,而未提交的数据可能会被回滚,所以我们称这个级别读取的数据为脏数据,这种问题称为脏读。2.READCOMMITTED读已经提交,也叫提交读,这个隔离级别的事务可以读取提交事务的数据,所以不会有脏读的问题。但是,由于在事务执行过程中可以读取其他事务提交的结果,所以同一个SQL查询在不同的时间可能会得到不同的结果。这种现象称为不可重复读。3.REPEATABLEREAD可以重复读取,MySQL默认的事务隔离级别。可重复读可以解决“不可重复读”的问题,但是仍然存在幻读问题。所谓幻读,就是当同一个事务在不同的时间使用同一个SQL查询时,会产生不同的结果。例如,执行两次但第二次返回第一次未返回的行的SELECT是“幻像”行。注:幻读和不可重复读的侧重点不同。不可重复读侧重于数据修改,同一行数据两次读取不同;幻读侧重于增删改查,两次查询返回的数据行数不同。4.SERIALIZABLE序列化,事务的最高隔离级别,它会强制对事务进行排序,这样就不会发生冲突,从而解决脏读、不可重复读和幻读的问题,但是由于执行效率低,实际使用场景并不多。5.总结简单总结一下,MySQL中的事务隔离级别就是为了解决脏读、不可重复读、幻读等问题。这四种隔离级别和这三个问题的对应关系如下:ReadUnrepeatableReadPhantomReadReadUncommitted(READUNCOMMITTED)√√√ReadCommitted(READCOMMITTED)×√√可重复读(REPEATABLEREAD)××√序列化(SERIALIZABLE)×××2.并发事务中的问题并发事务中存在以下三个问题。1、脏读一个事务读取另一个事务保存的数据commit,然后事务回滚,导致第一个事务读取了一个不存在的脏数据。2.不可重复读在同一个事务中,同一个查询在不同的时间得到不同的结果。例如事务在T1读取了某行数据,在T2再次读取该行时,该行的数据已经被修改,所以再次读取时,与T1查询得到的结果不同.3、幻读MySQL对幻读的定义如下:所谓幻读,是指同一个查询在不同的时间产生不同的行集合,在一个事务中发生的问题。例如,如果SELECT执行两次,但第二次返回第一次未返回的行,则该行是“幻像”行。官方文档:https://dev.mysql.com/doc/refman/5.7/en/innodb-next-key-locking.html翻译中文是:同一个查询在不同的时间得到不同的结果,就是这个问题交易中的幻读。例如,执行两次但第二次返回第一次未返回的行的SELECT是“幻像”行。三、隔离级别实践1、查询事务隔离级别查看全局MySQL事务隔离级别和当前会话的事务隔离级别的SQL如下:select@@global.tx_isolation,@@tx_isolation;以上SQL执行结果如下图所示:2.设置事务隔离级别每个连接MySQL的客户端都可以单独设置事务的隔离级别。MySQL可以使用如下SQL设置当前连接(客户端)的事务隔离级别:setsessiontransactionisolationleveltransactionisolationlevel;事务隔离级别有4个值:READUNCOMMITTEDREADCOMMITTEDREPEATABLEREADSERIALIZABLE3.脏读问题一个事务读取另一个事务保存的数据进行commit,然后事务回滚,导致第一个事务读到一个不存在的脏数据。接下来,我们使用SQL来演示脏读问题。在正式开始之前,先创建一张测试表:--创建一个城市表droptableifexistscity;创建表城市(idintprimarykeyauto_increment,namevarchar(250)notnull);脏读的执行顺序如下:dirtyread执行SQL和执行顺序如下:ClientA:setsessiontransactionisolationlevelreaduncommitted。客户A:开始交易。客户B:开始交易。客户端B:插入城市(名称)值('西安')。客户A:从城市中选择*。客户B:回滚。客户A:从城市中选择*。对应的执行结果如下图所示:从上面的结果我们可以看出,当客户端A设置为读未提交事务隔离级别时,客户端A可以读取其他事务未提交的数据。事务回滚后,客户端A读取的数据变成了脏数据,即脏读,即未提交事务隔离级别存在脏读问题。4.不可重复读问题在同一个事务中,同一个查询在不同的时间得到不同的结果。例如事务在T1读取了某行数据,在T2再次读取该行时,该行的数据已经被修改,所以再次读取时,与T1查询得到的结果不同.不可重复读的执行顺序如下:SQL和不可重复读的执行顺序如下:clientA:setsessiontransactionisolationlevelreadcommitted。客户A:开始交易。客户端A:从id=1的城市中选择*。客户B:开始交易。客户端B:更新citysetname='Chang'an'whereid=1。客户B:提交。客户端A:从id=1的城市中选择*。对应的执行结果如下图所示:从上面的结果可以看出,客户端A设置读取已提交事务隔离级别后,使用同一条SQL读取两次相同的数据内容是不同的.是的,这是不可重复读。即在读提交事务隔离级别,可能存在不可重复读问题。5.幻读问题同一个查询在不同的时间得到不同的结果,这就是事务中的幻读问题。例如,执行两次但第二次返回第一次未返回的行的SELECT是“幻像”行。幻读的执行顺序如下:幻读的SQL和执行顺序如下:ClientA:setsessiontransactionisolationlevelrepeatableread;客户A:开始交易;客户端A:select*fromcitywhereid<5;--查询1条数据ClientB:开始交易;客户端B:insertintocity(id,name)values(2,'北京');客户B:提交;ClientA:updatecitysetname='北京'whereid=2;客户端A:select*fromcitywhereid<5;--查询到执行对应的2条数据的结果如下图所示:从上面的结果可以看出,ClientA设置了能够重复读取的事务隔离级别后,相同SQL用于查询相同的结果。第一个查询产生一条数据,第二个查询产生两条数据。额外的数据行称为“幻像”。"行,所以可以得出可重复读可能存在幻读问题的结果。总结MySQL中有四种事务隔离级别:未提交读(脏读/不可重复读/幻读问题)、已提交读(非-repeatableread/phantomread问题)、repeatableread(幻读问题)和序列化,其中repeatableread是MySQL默认的事务隔离级别。dirtyreads读取其他事务未提交的数据,non-repeatablereads读取其他事务修改的数据,以及幻读读取其他事务添加或删除的“幻像”行数据。