你的面试官来找你,一个穿着格子衬衫,啤酒肚,发际线严重后退的中年男人。手里拿着泡了枸杞子的保温杯,腋下夹着一台MacBook,MacBook上贴着公司标语:“我爱加班”。采访开始,开门见山。面试官:看你简历上精通MySQL,我先问一下你的业务有什么特点?老生常谈了,还有谁不会背?我:这个我知道。事务具有四大特性,即原子性、一致性、隔离性和持久性,简称ACID。原子性意味着事务中的所有操作要么成功要么失败。一致性是指事务执行前后,数据始终处于一致状态,不会有数据丢失。隔离是指事务提交前的中间状态对其他事务是不可见的,即相互隔离。持久化是指事务提交后,对数据的修改永久保存在数据库中。面试官:嗯,很好的回答。那么你知道MySQL底层是如何实现事务的四大特性的吗?这道题有点深,需要背redolog,undolog,mvcc。别说你不知道这些东西是干什么用的。你不仅需要知道,还需要参与其中。我:原子性是通过undolog实现的,一致性是通过代码逻辑层次来保证的,隔离是通过mvcc实现的,持久化是基于redolog实现的。RedoLog(重做日志):RedoLog记录的是物理日志,即磁盘数据的修改。用于保证事务中改变的数据在服务崩溃后仍能持久化到磁盘。如果没有RedoLog会怎样?修改数据的过程变成这样:从磁盘加载数据到内存,修改内存中的数据,将新数据持久化到磁盘。这样做会导致严重的性能问题。InnoDB在磁盘上存储的基本单位是页面。这种修改可能只改变了一个页面中的几个字节,但是整个页面的数据都需要刷新,这是一种资源浪费。一个事务可能修改多个页面的数据,页面不连续,会产生随机IO,性能变差。于是为了提高写入性能,引入了RedoLog。看一下引入RedoLog后的修改过程:从磁盘加载数据到内存,修改内存中的数据,将新数据写入RedoLogBuffer,将RedoLogBuffer中的数据持久化到RedoLog文件,将数据持久化到RedoLog文件中数据库磁盘UndoLog(回滚日志):UndoLog记录逻辑日志,用于回滚事务时恢复修改前的数据。例如:当我们执行一条insert语句时,UndoLog记录了一条相反的delete语句。添加UndoLog后的修改过程是这样的:MVCC(Multi-VersionConcurrencyControl):记录了某个时间点的数据快照,用于实现不同事务之间的数据隔离。说到隔离,就不得不说到事务的隔离级别。在谈事务隔离级别之前,首先要说一下并发事务带来的问题:脏读:一个事务从其他事务中读取未提交的数据。不可重复读:同一个数据被多次读取,得到的结果集不一致,即读取的是其他事务提交的数据。幻读:对于同一个查询条件,多次读取的结果不一致,即读取的是其他事务提交后的数据。不可重复读和幻读的区别是:不可重复读读的是其他事务执行update和delete后的数据,而幻读读的是其他事务执行insert后的读数据。隔离级别ReadUnCommitted(读未提交):当从其他事务中读取未提交的数据时,会出现脏读、不可重复读和幻读。已提交读(ReadCommitted):读取其他事务提交的数据,解决脏读,会出现不可重复读和幻读。RepeatableRead:对于同一份数据,多次读取的结果集是一致的。解决了不可重复读之后,还是会出现幻读。Serializable:所有事务串行执行,解决幻读。再来说说MVCC:MVCC解决了读写冲突,实现了读写并行,提高了事务性能。由于ReadUnCommitted隔离级别,每次读取的都是最新的数据。在Serializable隔离级别下,所有读取的数据都被锁定。这两个隔离级别都不需要MVCC,所以MVCC只工作在ReadCommitted和RepeatableRead这两个隔离级别下。MVCC的实现通过两个隐藏列trx_id(最后提交事务的ID)和roll_pointer(上一个版本的地址)建立版本链。并在读入一个事务时生成一个ReadView(读视图)。在ReadCommitted隔离级别下,每次读取都会生成一个读取视图,而在RepeatableRead隔离级别下,只会读取第一次。生成阅读视图。InnoDB是如何解决幻读的?先普及一下snapshotread和currentread。当前读取:读取最新版本的数据并锁定数据。例如:insert、update、delete、selectforupdatesnapshotread:读取数据的历史版本,不锁定数据。例如:在当前读的情况下,select通过加锁解决幻读。在快照读取的情况下,幻读通过MVCC解决。面试官:还是得是你,只要你总结了一切。今天的面试就先来了,下次再问你关于MySQL锁的问题,你自己准备吧。本文知识点总结:文章持续更新中,大家可以在微信搜索“一光架构”第一时间阅读更多技术干货。
