本文转载自微信公众号《SH的全栈笔记》,作者SH的全栈笔记。转载本文请联系SH全栈笔记公众号。之前发过一篇文章,简单了解一下MySQL中的相关锁。里面提到如果我们使用的MySQL存储引擎是InnoDB,它的事务隔离级别是RR可重复读,就可以避免幻读。但是没想到1202还有人议论纷纷,说InnoDB的RR隔离级别下会出现幻读,只能靠gap和next-key这两个锁来防止幻读阅读。一开始我以为是他。如果你不知道这一点,你就和他聊天,最后聊完之后你发现你只是为了糊口。这个在后面讲可重复读的隔离级别的时候会讲到。本来觉得事务隔离级别太简单不好说了,但是经过上面的事件,我打算详细讲解一下事务隔离。接下来顺便说一下InnoDB的所有事务隔离级别。ACID在谈事务隔离级别之前,我们需要了解一下ACID模型。ACID模型表示:原子性一致性ConsistencyIsolationIsolationDurabilityPersistence原子性是指在InnoDB事务中,所有操作要么全部成功要么全部失败,不会处于中间状态。更笼统地说,如果事务A失败,则它所做的所有更改都应该回滚。一致性主要是为了保护数据的一致性,防止数据库崩溃导致的数据一致性问题。例如,当我们更新MySQL数据时,更新后的数据会先到达InnoDB的BufferPool中。如果此时MySQL所在的机器突然意外重启,如果InnoDB没有崩溃恢复机制,那么之前更新的数据就会丢失。一致性问题出现了。许多其他博客写的是交易开始前后数据的完整性。我说看不懂,太抽象了。隔离主要是指事务之间的隔离,更具体地说,就是我们本文要讨论的事务隔离级别。持久化主要是指我们增加或删除了一些数据。一旦成功,这些操作应该需要持久化到磁盘。ACID模型可以理解为一种数据库设计范式,主要关注数据及其自身的可靠性。MySQL中的InnoDB完全遵循ACID模型,在存储引擎层支持数据一致性校验和崩溃恢复机制。ACID中的隔离类型是我们本文讨论的重点。事务隔离级别有很多文章直接介绍了事务隔离级别的类型,这种类型是什么意思,那种类型怎么用。但是我觉得首先应该明白为什么需要事务隔离级别,事务隔离级别解决什么问题,这个才是关键。我们知道在InnoDB中会有多个事务同时对数据进行操作。下面举几个例子:如果事务A需要查询id=1的数据,但是事务A查询完成后,事务B更新了id=1的数据,那么此时事务A又执行了查询。你应该看到更新前的数据还是更新后的数据?或者上面的例子,事务A读取了事务B的数据,但是如果事务B回滚了怎么办?事务A数据不是变成脏数据了吗?或者事务A读取了1-3点的schedule,有4个item,但是事务A读取后,事务B在1-3点的时间段又插入了一个新的安排,如果事务A读取再一次,它应该显示4个时间表还是5个?以上问题需要事务隔离级别来回答。其实以上三种情况对应的是不可重复读、脏读、幻读。InnoDB通过事务隔离级别分别解决了上述问题。所有事务隔离级别如下:READUNCOMMITTEDreaduncommittedREADCOMMITTEDreadcommittedREPEATABLEREADrepeatablereadSERIALIZABLESerializedInnoDB默认的事务隔离级别是REPEATABLEREAD。Readuncommitted事务A读取事务B未提交的数据,如果此时事务B出错回滚,事务A读取的数据就变成了脏数据,导致脏读。如果事务B更新事务A读取的数据,事务A再次读取,读取的是事务B修改的结果,造成不可重复读。而如果事务B新增了数据,事务A再次读取,就会读取到事务B的新数据,从而造成幻读。所以综上所述,在readuncommitted隔离级别下,会造成以下问题:脏读,不可重复读,幻读,committed事务A读取了事务B提交的数据,如果事务B更新了事务读取的数据A,并提交,那么当事务A再次读取时,会读取其他事务的变化,造成不可重复读。同理,如果事务B新增数据提交,事务A再次读取时获取的是事务B刚刚提交的数据,从而造成幻读。所以综上所述,在read-committed隔离级别下,会导致:non-repeatablereadphantomreadrepeatableread事务A不会读取事务B更新的数据,也不会读取事务B在available中新增的数据在重复读场景下,不会出现脏读,也不会出现不可重复读,可能会出现幻读。不管事务B做了什么操作,事务A查询到的id=1的数据都是张三。但是,在某些情况下,仍然可能会出现幻读。重复读只能在某些情况下产生幻读,但InnoDB绝对不是不能避免幻读。首先,InnoDB在RR隔离级别下有一个明确的解决幻读的方法,那就是——相邻键锁,一种结合了间隙锁和记录锁的锁。下面举个例子看看在RR隔离级别下,什么情况下会出现幻读,什么情况下不会出现幻读。首先是可能会出现幻读。SELECT*FROM`student`WHERE`id`>1由于InnoDB有MVCC进行多事务并发,此时SELECT使用的是快照读取,没有加锁,所以key锁就无法发挥作用。如果有其他事务插入了一条数据,那么事务再执行上面的语句,就有可能查出id>1的数据。但是如果显示被锁住,就可以避免这个问题。SELECT*FROM`student`WHERE`id`>1FORUPDATE至于为什么keylock可以避免幻读,前面的文章已经讲的很清楚了,这里不再赘述。序列化,让事务强制串行执行,从根本上避免了并发问题,但是这样会降低MySQL的性能。因为现在同时运行的只有一个事务。EOF这里先介绍一下事务隔离级别,以后有空再说说事务隔离级别的底层原理。
