文章部分总结来自课程,非原创
MySQL 组织架构
下面这张图就可以解释关于 MySQL 底层的组织架构了。
上面的图可以直观地展示两个重要的东西 :
- 一条 SQL 的执行流程
- MySQL 的底层架构
大体来说,MySQL可以分为Server层和存储引擎层两部分。
Server 层
Server层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖MySQL的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。
存储引擎层
存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持InnoDB、MyISAM、Memory等多个存储引擎。现在最常用的存储引擎是InnoDB,它从MySQL 5.5.5版本开始成为了默认存储引擎。 也就是说,你执行create table建表的时候,如果不指定引擎类型,默认使用的就是InnoDB。不过,你也可以通过指定存储引擎的类型来选择别的引擎,比如在create table语句中使用engine=memory, 来指定使用内存引擎创建表。不同存储引擎的表数据存取方式不同,支持的功能也不同,在后面的文章中,我们会讨论到引擎的选择。
redo-log 和 bin-log
这两个log 日志文件主要是用作持久化功能的,那么它们各自恢复什么数据呢?并且这两者之间有什么差异呢? 例子 : 一个古代的酒店,掌柜有个账本,客人来吃饭需要记账,但是有时客人很多,来不得及一下子全部记下去,并且有个问题,例如张三今天过来消费,那么掌柜需要先找到账本中张三的总账,然后再在上面加上一笔, 那样在客人多的时候根本来不得处理,于是掌柜就拿出了小黑板,当客人多的时候就先往小黑板上记录,然后闲的时候再从小黑板移入账本,但是小黑板的空间毕竟有限,那么当写不下的时候掌柜就先把小黑板的数据搬入到 账本中,然后小黑板的数据擦掉,可以让小黑板继续记账。
redo-log
上面例子中的小黑板就是 redo-log ,而 bin-log 就是账本。我们刚才讲到小黑板有满的时候,那么“满了”的临界点在redo-log 中就是 checkpoint 。InnoDB的redo log是固定大小的,比如可以配置为一组4个文件,每个文件的大小是1GB,那么这块“粉板”总共就可以记录4GB的操作。从头开始写,写到末尾就又回到开头循环写,如下面这个图所示。
而粉板和账本配合的整个过程,其实就是MySQL里经常说到的WAL技术,WAL的全称是Write-Ahead Logging,它的关键点就是先写日志,再写磁盘,也就是先写粉板,等不忙的时候再写账本。 具体来说,当有一条记录需要更新的时候,InnoDB引擎就会先把记录写到redo log(粉板)里面,并更新内存,这个时候更新就算完成了。同时,InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做,这就像打烊以后掌柜做的事。 有了redo log,InnoDB就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe。例如MySQL在提交事务的时候,提交了一半,服务器就down 了,那么 redo-log 依旧还保持着这条还未提交的事务。
bin-log
前面我们讲过,MySQL整体来看,其实就有两块:一块是Server层,它主要做的是MySQL功能层面的事情;还有一块是引擎层,负责存储相关的具体事宜。上面我们聊到的粉板redo log是InnoDB引擎特有的日志,而Server层也有自己的日志,称为binlog(归档日志)。
我想你肯定会问,为什么会有两份日志呢?
因为最开始MySQL里并没有InnoDB引擎。MySQL自带的引擎是MyISAM,但是MyISAM没有crash-safe的能力,binlog日志只能用于归档。而InnoDB是另一个公司以插件形式引入MySQL的,既然只依靠binlog是没有crash-safe能力的,所以InnoDB使用另外一套日志系统——也就是redo log来实现crash-safe能力。
两个日志的区别
redo log是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。
redo log是物理日志,记录的是“在某个数据页上做了什么修改”(k改为5 ,k改为 6 ,k 改为 7 );binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1 ”(k = 2 ,k= k+1 , k= k*k )。
redo log是循环写的,空间固定会用完;binlog是可以追加写入的。“追加写”是指binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
执行流程
mysql> create table T(ID int primary key, c int); mysql> update T set c=c+1 where ID=2;
有了对这两个日志的概念性理解,我们再来看执行器和InnoDB引擎在执行这个简单的update语句时的内部流程。
执行器先找引擎取ID=2这一行。ID是主键,引擎直接用树搜索找到这一行。如果ID=2这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
执行器拿到引擎给的行数据,把这个值加上1,比如原来是N,现在就是N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,此时redo log处于prepare状态。然后告知执行器执行完成了,随时可以提交事务。
执行器生成这个操作的binlog,并把binlog写入磁盘。
执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(commit)状态,更新完成。
这里我给出这个update语句的执行流程图,图中浅色框表示是在InnoDB内部执行的,深色框表示是在执行器中执行的。
参考资料
- MySQL45实战