先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Golang全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024b (备注go)
正文
熊猫(老脸一红):emmm…精通就是,比面试官知道的多一点呗。。
面试官:(嗯。。。。是个老实人,我喜欢!)
面试官:那你先给我讲讲MySQL里有哪些比较重要的日志吧?
熊猫:(我只看了redo log、binlog面试题,咋整,多说会不会给自己挖坑?记得鲁迅大爷说过:别啥JB都说,最后坑自己)
熊猫:嗯。。其实MySQL中的日志有很多,但日常接触最多的是重做日志(redo log)、归档日志(binlog)这两种,当然还有回滚日志(undo log)等等,我接触的少一些。
MySQL日志主要包括六种:
- 重做日志(redo log)
- 回滚日志(undo log)
- 归档日志(binlog)
- 错误日志(errorlog)
- 慢查询日志(slow query log)
- 一般查询日志(general log)
- 中继日志(relay log)
面试官:好,那你先说一下你对 redo log
日志的理解吧。
熊猫:记得小时候看《武林外传》,吕秀才柜台下面有一个小黑板,当时不知道是干啥的,后来发现是专门用来记录客人的赊账记录。如果赊账的人不多,那么他可以把顾客名和账目写在板上。但如果赊账的人多了,小黑板没地儿了,这个时秀才一定还有一个专门记录赊账的账本。如果有人要赊账或者还账的话,秀才一般有两种做法:
- 一种做法是直接把账本翻出来,把这次赊的账加上去或者扣除掉;
- 另一种做法是先在小黑板上记下这次的账,等打烊以后再把账本翻出来核算。
在生意火爆时,秀才肯定会选择后者,因为直接记账本太麻烦了。得先翻出赊账人“老钱”那条记录,账本密密麻麻几十页,找到后再拿出算盘计算,最后更新到账本上。想想都麻烦。相比之下,还是先在小黑板上记一下方便。你想想,如果秀才没有小黑板的帮助,每次记账都得翻账本,效率是不是低得让人难以忍受?还有时间泡小郭?想无双?
同样,在 MySQL 里也有这个问题,如果每一次的更新操作都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程 IO 成本、查找成本都很高。为了解决这个问题,MySQL 的设计者就用了类似秀才记小黑板的思路来提升更新效率。
而小黑板和账本配合的过程,其实就是 MySQL 里经常说到的 WAL 技术
。
面试官:小伙子你这思路很奇特呀!那你再详细跟我说一下,啥是WAL技术?
熊猫:(小马哥对我有意思啊!)
WAL 的全称是 Write-Ahead Logging,它的关键点就是先写日志,再写磁盘,也就是先写小黑板,等不忙的时候再写账本。
具体来说,当有一条update语句要执行的时候,InnoDB 引擎就会先把记录写到 redo log(小黑板)里面,并更新内存,这个时候更新就算完成了。
同时,InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做,这就像打烊以后秀才做的事。如果今天赊账的不多,掌柜可以等打烊后再整理。但如果某天赊账的特别多,小黑板写满了咋办?这个时候秀才只好叫无双帮忙干自己的活儿,抓紧把小黑板中的一部分赊账记录更新到账本中,然后把这些记录从小黑板上擦掉,为记新账腾出空间。
与此类似,InnoDB 的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 100MB,那么这块“小黑板”总共就可以记录 400MB 的操作记录。从头开始写,写到末尾就又回到开头循环写,如下面这个图所示。
- write position 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。
- checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。
write position 和 checkpoint 之间的是“小黑板”上还空着的部分,可以用来记录新的操作。
如果 write pos 追上 checkpoint,表示“小黑板”满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。
有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe
。
crash-safe:
可以对照前面赊账记录的例子。只要赊账记录记在了小黑板上或写在了账本上,即使秀才突然被老邢抓走几天,回来后依然可以通过账本和小黑板上的数据明确赊账账目。就是维护数据的持久性。
本质上说,crash-safe 就是落盘处理,将数据存储到了磁盘上,断电重启也不会丢失。
面试官:不错,你这理解虽说听的我一愣一愣,但是话糙理不糙,确实说出了redo log的原理。那你再说说对binlog日志的理解吧。
熊猫: 嘿嘿,谢谢马经理夸奖。MySQL 其实是分为 server层 和 引擎层两部分。
- Server 层:它主要做的是 MySQL 功能层面的事情;
- 引擎层:负责存储相关的具体事宜。
上面我们聊到的“小黑板” redo log 是 InnoDB 引擎特有的日志
,而 Server 层也有自己的日志,称为binlog(归档日志),其实就是用来恢复数据用的。
面试官:那MySQL为啥要有redo log 和 binlog两个日志呢?只留一个不香么?
熊猫:因为最开始 MySQL 里并没有 InnoDB 引擎。MySQL 自带的引擎是 MyISAM,但是 MyISAM 没有 crash-safe 的能力,而 binlog 日志只用于归档。
InnoDB 是另一个公司以插件形式引入 MySQL 的。我们知道,只依靠 binlog 是没有 crash-safe 能力的,所以 InnoDB 使用另外一套日志系统——也就是 redo log 来实现 crash-safe 能力。
面试官:那这两个日志主要有哪些区别?
熊猫:emmm…主要有几下几种区别:
- redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎共用。
- redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=1 这一行的 c 字段加 1 ”。
- redo log 是循环写的,空间固定会用完然后复写;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
面试官:好,基于你上面说的,那比如下面这条SQL,你来描述一下在MySQL内部的执行流程吧
update T set money = money + 500 where username = ‘陈哈哈’;
熊猫:
- (开始,原始数据接入)执行器先找引擎取 username = ‘陈哈哈’ 这一行。如果 username = ‘陈哈哈’ 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
- (数据修改)执行器拿到引擎给的行数据,把 money 这字段的值加上 500,比如原来是 N,现在就是 N+500,得到新的一行数据,再调用引擎接口写入这行新数据。
- (数据提交)提交操作,由于存储引擎层与server层之间采用的是内部XA(保证两个事务的一致性,这里主要保证redo log和binlog的原子性),所以提交分为prepare阶段与commit阶段,也就是我们说的
两阶段提交
。 - (写redo log)引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面(写到内存或直接落盘),到这里, redo log 处于 prepare 状态。
- (写binlog)然后告知执行器执行完成了,随时可以提交事务。执行器生成这个操作的 binlog,并把 binlog 同步到磁盘。
- (数据更新到磁盘或内存,结束)执行器调用引擎的提交事务接口执行修改操作,需要将在二级索引上做的修改,写入到change buffer page,等到下次有其他sql需要读取该二级索引时,再去与二级索引做merge,引擎把刚刚写入的 redo log 标记上(commit)状态,实际上是加上了一个与binlog对应的XID,使两个日志逻辑保持一致,到此结束,更新流程闭环。
面试官:那为啥必须要分成prepare和commit两个阶段进行提交呢?一块儿提交他不爽么。
熊猫:我举个现实生活中的栗子吧,一个完整的交易过程我认为应该这样:
比如你来我的小超市里买一瓶可乐:
- 小马哥:老板给我来瓶可乐!透心凉心飞扬的那个。
- 我:??
- 机器扫一下可乐,告诉小马哥这瓶可乐2块5,不能白嫖,让他给钱(记录 redo log,事务处于prepare状态)
- 收钱放入钱箱(记录 binlog,
事务实际是否完成的根本依据
,处于待标记commit阶段) - 然后让你把可乐拿走(redo log 状态标为 commit,表示该事务逻辑闭环)。
到这里,代表一笔交易结束
。 - 并告诉小马哥,透心凉心飞扬那个是雪碧,你个憨X~
- 等算账前再把这一天卖东西的交易信息一起同步到数据库。
可见,如果收钱之前(prepare阶段,步骤3)交易被打断,回过头来处理此次交易,发现只有记了小黑板但没有收钱,则交易失败,删掉小黑板上的记录(回滚);
如果收了钱后(commit阶段 或 待commit阶段,步骤4 || 5)交易被打断,然后回过头发现系统上有记录(prepare)而且钱箱有本次收入(bin log),则说明本次交易有效,补充修改commit状态,更新到库存中。
以上是人话,咱们再来看看MySQL层面的专业解释:
这里我们用反证法来进行解释为何需要两阶段提交。由于 redo log 和 binlog 是两个独立的逻辑,如果不用两阶段提交,要么就是先写完 redo log 再写 binlog,或者采用反过来的顺序。我们看看这两种方式会有什么问题。
仍然用前面的 update 语句来做例子。假设当前 username = ‘陈哈哈’ 的行,账户余额字段 money 的值是 100,再假设执行 update 语句过程中在写完第一个日志后,第二个日志还没有写完期间发生了 crash(异常宕机),会出现什么情况呢?
依旧以这条SQL为例:
update T set money = 0 + 500 where username = ‘陈哈哈’;
1、先写 redo log 后写 binlog。
假设在 redo log 写完,binlog 还没有写完的时候,MySQL 进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 money 的值是 money + 500。
但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句。
然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 money 的值就是 0,与原库的值不同。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-ldTmoKIX-1713474301630)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!