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

新的MySQL事务隔离级别!

时间:2023-03-18 01:58:25 科技观察

简介大家在面试的时候肯定遇到过事务的隔离级别吧?说实话,事务隔离级别的问题,不管是校招还是社招,面试官都爱问!但是,网上有很多文章。说实话,看完之后,我怀疑作者有没有看懂!因为他们对可重复读(RepeatableRead)和可序列化(serializable)的分析实在是让我一头雾水!另外很多书上都说可重复读解决幻读的问题,比如《mysql技术内幕--innodb存储引擎》等,我就不一一列举了,所以网上大部分关于事务隔离级别的文章都是有问题的,所以我会另写一篇文章来解释!本文大部分内容均有官网支持,所以大家阅读本文内容,可以作为一个概念牢记在心。除非官网上的开发手册有错,否则应该是正确的!另外,本文将重点讨论可重复读(RepeatableRead)是否真的能解决幻读问题!在正文开头,我会提到现在,根据事务的隔离级别,会出现三种情况。脏读、不可重复读和幻读。这三种情况的定义我这里就不说了,后面讲隔离级别的时候再补充。这里大家记住,根据脏读、不可重复读、幻读的定义(自己总结的,没有官网),有如下包含关系:那么,这张图怎么理解呢?也就是说,如果发生了脏读,那么一定会发生不可重复读和幻读。因为脏读现象,也可以解释不可重复读和幻读的定义。但是反过来,拿不可重复读这个现象来说,脏读的定义也未必能解释清楚!假设有一个表tx_tb如下,pId是读未提交的主键READ_UNCOMMITTED,其实这个从隔离名就可以看出,一个事务可以从另一个事务读取未提交的数据!为了便于说明,我简单画了一张图!如图所示,一个事务取回的数据被另一个未提交的事务修改。官网在https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_dirty_read定义了脏??读,其内容是脏读,一种检索不可靠数据的操作,数据被另一个事务更新但尚未承诺。翻译的意思是检索操作得到的数据是不可靠的,可以被另一个未提交的事务修改!你会发现我们的演示结果和官网定义的脏读是一致的。根据我们最初的推理,如果有脏读,那么一定存在不可重复读和幻读。读提交是READ_COMMITTED,这也可以看出,一个事务可以读取另一个事务提交的数据!为了便于说明,我就简单画个图来说明一下!如图所示,一个事务取回的数据只能被另一个事务读取一个提交的事务修改。不可重复读定义官网地址为https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_non_repeatable_read其内容为不可重复读查询获取数据时的情况,以及稍后在同一事务中的查询检索应该是相同数据的内容,但查询返回不同的结果(同时由另一个事务提交更改)。翻译过来就是一个查询语句检索数据,然后在同一个事务中用另一个查询语句检索数据,两次数据应该是一样的,但实际情况返回的结果不同。(被同时提交的另一个事务修改)!ps:作者注,这里的不同结果指的是行没有变化的情况(专业的说主键索引没有变化),但是主键索引指向的磁盘上的数据内容发生了变化。如果主键索引发生变化,比如增加一条数据或者删除一条数据,则不是不可重复读。显然,我们的现象符合不可重复读的定义。接下来我们思考一下:对于脏读现象,不可重复读的定义有意义吗?显然,脏读的现象,也就是读未提交的例子,是不是也符合同一个事务返回不同的结果呢!当一个事务B发生变化时,如果事务B在没有提交的情况下改变了事务A的结果,则为脏读,不可重复读。如果事务B提交更改事务A的结果,则不是脏读,而是不可重复读。可重复读取是REPEATABLE_READ。这里,我换个顺序,先去幻读的定义。幻读定义在官网的地址是https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_phantophantom一个查询的结果集中出现的一行,但结果中没有一组较早的查询。例如,如果一个查询在一个事务中运行两次,同时,另一个事务在插入新行或更新行之后提交,以便它与查询的WHERE子句匹配。翻译过来就是某行数据出现在一次查询的结果集中,但是该数据没有出现在更早的查询结果集中。比如一个事务中进行了两次查询,另一个事务插入一行或者更新一行数据(数据满足查询语句中where后面的条件),提交!好了,接下来就是上图,大家判断这个现象是否符合幻读的定义。显然,这种现象符合幻读的定义。同一事务中的两个相同查询会出现不同的行。现在,我们想一想:幻读的定义在上面的不可重复读现象中有意义吗?每个人都为自己着想!反过来不一定有效。事务第二次查询一条数据,第一次查询的结果集中没有出现该数据。如果数据是被修改过的数据,那么这种现象既属于不可重复读,也属于幻读。如果数据是新增或删除的数据,那么这种现象就不是不可重复读,而是幻读。接下来说说为什么很多文章都是曲解的,说可重复阅读可以解决幻读的问题!原因来自官网的一句话地址:https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-record-locks原文内容如下默认情况下,InnoDB在REPEATABLEREAD事务隔离级别运行。在这种情况下,InnoDB使用next-key锁进行搜索和索引扫描,以防止出现幻象行(请参阅第14.7.4节,“幻象行”)。按照原句的意思,应该是InnoDB默认使用REPEATABLEREAD。在这种情况下,使用next-keylocks来解决幻读问题!结果估计是国内的一个译者翻了翻,把InnoDBusingREPEATABLEREADbydefault改成了InnoDB。这样的话,幻读的问题就可以解决了!然后大家继续抄我我抄你,你懂的!显然,在“使用下一键锁!”的条件之后。省略了,意思就完全变了。我们在这个隔离级别下执行语句select*fromtx_tbwherepId>=1是一个没有任何锁的快照读,根本解决不了幻读的问题,除非你使用select*fromtx_tbwherepId>=1lockinsharemode;这样,你将使用next-keylocks就可以解决幻读问题!串行读取是SERIALIZABLE_READ。在这个隔离级别下,共享模式的锁会自动加在所有select语句之后。因此,在这种隔离级别下,无论你怎么查询,都会使用next-key锁。当前已读取所有选择操作!OK,注意上表红色部分!是因为在这个隔离级别下使用了next-key锁,innodb记录了pId=1的索引记录,而(1,++∞)这个间隙被锁住了。如果其他事务要往这个空隙插入数据,就会被阻塞,防止幻读的发生!有人会说,你第二次查询的结果也变了,跟第一次查询的结果明显不一样啊?对此,我只能说,请大家看清楚。这是你自己的事情改变的,不是别的事情改变的。这不是幻读,也不是不可重复读。综上所述,上面说了一大堆,***来一张表做个总结,面试的时候可以回答这张表。以上都是为这张桌子准备的!