面试官:“你好,我是XX面试官,请问是小龙吗?”
小龙:“您好,面试官,我是小龙”
面试官:“好的,现在有空吗,我们开始面试吧”
小龙:“嗯嗯,准备好啦”
.......
other questions
.......
面试官:“好的,我们先根据你简历聊聊哈,我看你有提到熟悉 MySQL的使用吗 ?。”
小龙:“哈哈,还算可以吧!”
面试官:“那你能告诉我 MySQL 大体的一个架构是怎样的吗?有去了解过吗?”
小龙:“MySQL 分为 Server 层与 存储引擎层,Server 层包含连接器、分析器、优化器、执行器。”
面试官:“那现在假如我执行一条 SQL 语句 select name from xl_tb where 主键ID=1,你觉得 MySQL 底层是怎样去解析执行这条 SQL 的呢?”
小龙:“首先要由我们的客户端去连接服务器,需要连接器进行身份验证,权限认证。”
小龙:“然后在MySQL8.0之前,如果开启了缓存,执行这条 SQL 的话会首先以这条 SQL 为 key 去缓存查询是否有对应的 value,如果有直接返回结果,MySQL8.0 在综合考虑其优劣后移除了缓存。”
面试官:“嗯嗯,那假如没有开启缓存,或者没有该 SQL 映射的结果呢?”
小龙:“那么便会正式解析这条 SQL 去查询结果啦。”
面试官:“能简单说说是怎样解析 SQL 的吗?”
小龙:“服务器肯定不会什么语句都给你去执行一遍,首先它会利用分析器通过词法语法分析看这条语句是否符合它的语法规则,并解析关键词等”
小龙:“如果语法没问题,也不能就这么拿去执行了,因为要考虑执行代价成本之类的。”
面试官:“那它内部是怎么做的呢?”
小龙:“MySQL 会通过优化器生成该 SQL语句的执行计划,我们可以通过 explain 命令查询。然后若你用到了索引可能还会选择索引,它会选择最优的方案去执行,因为即使你使用了索引,在某些情况可能也没有全表扫描来的快。”
小龙:“这里,肯定就走主键索引,根据 ID=1 去查。”
面试官:“enen,那得到最优执行方案后,最后是怎样执行的呢?”
小龙:“最后语句来到执行器,打开表调用存储引擎接口,逐行判断是否满足查询条件,当然如果用到索引,会根据索引来筛选,速度也会更快,这里便是利用主键索引去查询 ID=1 的结果。”
小龙:“然后把满足条件的放到结果集,最后返给客户端显示。”
面试官:“嗯嗯,理解的还是很清楚。OK,知道 MySQL 事务的四大特性吗?”
独白:“这个就很基础。”
小龙:“ ACID,原子性,隔离性,持久性,一致性”
面试官:“OK,那你知道它们底层是怎样实现的吗,随便挑两个讲讲?”
独白:“这个在我【面试笔记】中详细总结过啦,随便给面试官举几个吧。”
小龙:“比如原子性吧,我们知道实现原子性的关键是,事务回滚时能够撤销所有已经成功执行的 SQL 语句。”
小龙:“而当事务对数据库进行修改时,InnoDB 会生成对应的 undo log,undo log 会保存事务开始前老版本的数据,当事务发生异常,便会 rollback 回滚到老版本状态。”
面试官:“那它是怎样回滚的呢?”
小龙:“InnoDB 会根据 undo log 的内容做相反逻辑操作,比如:insert 语句,回滚时会执行 delete,update 语句,回滚时便执行相反的 update,把数据改回来。”
小龙:“总之,原子性主要便是靠 undo log 保证 。它是一种回滚日志,不仅可以用来保证原子性,还可以用来实现隔离性 MVCC。”
小龙:”MYSQL的持久性便是由redo log来保证。它可以记录事务开启后对数据做的修改,并且可以进行 crash-safe,数据库异常断电等情况可用 redo log恢复。“
面试官:“你知道这里面用到的一种 WAL 技术吗?”
小龙:”MySQL 利用的 WAL 技术,全称是Write-Ahead Logging 。关键点就是先写日志,再写磁盘。“
小龙:“可以把 redo log 比作一个卸货的小推车,我们卸货若是每下一件物品就拿着去入库,那岂不是特浪费时间(效率低、还要找到合适存库位置)”
小龙:“此时,若有一个小推车,我们将货物首先存放在小推车,当推车满了再往库里存,岂不大大增加了效率。”
小龙:“我们在更新数据库时,就是先将更新操作记录在redo log日志,等 redo log 满了或则 MYSQL 空闲了再刷盘。”
独白:“由于篇幅原因,这里没有详细归纳总结,有兴趣的可以看【面试笔记】,里面对相关核心重点大厂常考的都详细总结啦。”
面试官:“OK,由于时间原因,这块我们就聊到这,你知道事务中存在的一些并发问题吗?”
小龙:“理论上会存在脏读、不可重复读、幻读,但是可以通过使用不同隔离级别就行控制的。”
面试官:“能给我讲讲什么是幻读吗?”
小龙:“举个简单例子,比如事务A 按照一定条件进行数据读取, 期间事务B 插入了相同搜索条件的新数据,事务A再次按照原先条件进行读取时,发现了事务B 新插入的数据 称为幻读。”
面试官:“那你能告诉我MySQL 是怎样去解决幻读的吗?或者根本就还没解决,谈谈你的理解?”
独白:“由于篇幅原因,这里只做简单总结,有兴趣的可以看【面试笔记】,通过实验详细进行了分析,可以帮助更好的理解。”
友情提示:不要相信网上众说纷纭的帖子,你抄我我抄你,一会这个说RR隔离级别彻底解决了幻读,一会那个说没有。最好自己亲自做实验看看,结果一目了然。
我当时为了这个困惑,百度都查烂了,帖子满天飞,答案混淆视听,最后干脆自己做实验最后得出正确结论,并详细记录在【面试笔记】中。
小龙:“RR隔离界别并没有完全解决幻读,只使用快照读(使用MVCC解决了幻读)或则当前读(间隙锁解决幻读)不会幻读。若先快照读,然后当前读,期间按快照读相同条件插入数据,当前读就又会发生幻读。
当时秋招网易云二面,我清楚记得我凭借这个问题彻底征服了面试官,从头到位给他清楚分析了一遍,最后即使我二本也拿了SP,不过拒了。
面试官:“真的不错,顺便问一下,你能简单说说隔离级别机制实现原理吗?”
可能有些同学会说这些不会问,但是很多面试都会拐着弯问你这,只是你不知道。
当时我面试时面试官没有直接这样问,我是当它问了事务开始我就一步一步留线索让它顺着我拿手的知识问,由于这样写文章会很长,我就直接上问题了。
小龙:“在读未提交下,可以直接读取数据,并不能解决任何并发问题。”
小龙:“而可串行化,它主要使用锁,读加共享锁,写加排他锁,串行执行来实现。”
小龙:“而读已提交和可重复读实现原理就是MVCC Read View不同的生成时机。可重复读只在事务开始时生成一个Read View,之后都用的这个;读已提交每次执行前都会生成Read View。”
独白:“这里关于 Read View 就不展开讲了,由于篇幅有限。改期再拿出来讲。”
面试官:“好的,时间差不多啦,今天暂时聊那么多,下期再谈谈。”
独白:“不愧是我,真男人是也!”
本文转载自微信公众号「小龙coding」,可以通过以下二维码关注。转载本文请联系小龙coding公众号。