转自:
https://mp.weixin.qq.com/s/wr2gJGQSA8QH_lmPh1XOkw
如下图所示,MySQL 架构图。
如下图所示, InnoDB 架构图。
InnoDB 内存架构
从上面第二张图可以看到,InnoDB 主要分为两大块:
- InnoDB In-Memory Structures(内存)
- InnoDB On-Disk Structures(磁盘)
Buffer Pool
The buffer pool is an area in main memory where InnoDB caches table and index data as it is accessed.
正如我们所知,MySQL 不会直接去修改磁盘的数据,因为这样做太慢了,MySQL 会先改内存,然后记录 redo log,等有空了再刷入磁盘,如果内存里没有数据,就去磁盘 load。
而这些数据存放的地方,就是 Buffer Pool。
我们平时开发时会用 Redis 来做缓存,目的是缓解数据库的压力,其实 MySQL 自己也做了一层类似缓存的东西。
MySQL 是以页(page)为单位从磁盘读取数据的,Buffer Pool 里的数据也是如此,实际上,Buffer Pool 是一个以页为元素的链表。
为什么是链表?
因为和缓存一样,它也需要一套淘汰算法来管理数据。
Buffer Pool 采用基于 LRU(least recently used) 的算法来管理内存。
Change Buffer
上面提到过,如果内存里没有对应页的数据,MySQL 就会把数据从磁盘里 load 出来,但如果每次需要的页都不同,或者都不是相邻的页,那么每次 MySQL 都要去 load,这样就很慢了。
于是如果 MySQL 发现你要修改的页不在内存里,就把要对页的修改,先记到一个叫 Change Buffer 的地方,同时记录 redo log,然后再慢慢把数据 load 到内存,load 过来后,再把 Change Buffer 里记录的修改,应用到内存(Buffer Pool)中,这个动作叫做 merge(Change Buffer -> Buffer Pool),而把内存数据刷到磁盘的动作,叫 purge(Buffer Pool -> Disk)。
The change buffer is a special data structure that caches changes to secondary index pages when those pages are not in the buffer pool. The buffered changes, which may result from INSERT, UPDATE, or DELETE operations (DML), are merged later when the pages are loaded into the buffer pool by other read operations.
上面是 MySQL 官网对 Change Buffer 的定义,仔细看的话,你会发现里面提到 Change Buffer 只在操作二级索引(secondary index)时才使用。
原因是聚簇索引(clustered indexes)必须是唯一的,这也就意味着每次插入或更新,都需要检查是否已经有相同的字段存在,也就没有必要使用 Change Buffer 了。另外,聚簇索引操作的随机性比较小,通常是在相邻的页进行操作,比如使用了自增主键的聚簇索引,那么 insert 时就是递增、有序的,不像二级索引那样,访问非常随机。
Adaptive Hash Index
MySQL 索引不管是在磁盘里还是被 load 到内存,都是 B+ 树,而 B+ 树的查找次数取决于树的高度。你看,数据都已经放到内存了,还不能“一下子”就找到它,还要“几下子”,这空间牺牲的是不是不太值得?
尤其是那些被频繁访问的数据,每次过来都要走 B+ 树查询,这时就会想到,我用一个指针把数据的位置记录下来不就好了?
这就是自适应哈希索引(Adaptive Hash Index)。
自适应,顾名思义,MySQL 会自动评估使用自适应索引是否值得,如果观察到建立哈希索引可以提升速度,则建立。
Log Buffer
The log buffer is the memory area that holds data to be written to the log files on disk.
从上面的架构图可以看到,Log Buffer 里的 redo log,会被刷到磁盘里。
Operating System Cache
在内存和磁盘之间,你看到 MySQL 画了一层叫做 Operating System Cache 的东西,其实这个不属于 InnoDB 的能力,而是操作系统为了提升性能,在磁盘前面加的一层高速缓存。
InnoDB 磁盘架构
磁盘里有什么呢?
除了表结构定义和索引之外,还有一些为了高性能和高可靠而设计的角色,比如 redo log、undo log、Change Buffer 以及 Doublewrite Buffer 等。
表空间(Tablespaces)
从架构图中可以看到,Tablespaces 分为五种:
- The System Tablespace
- File-Per-Table Tablespaces
- General Tablespace
- Undo Tablespaces
- Temporary Tablespaces
其中,我们平时创建的表的数据,可以存放到 The System Tablespace 、File-Per-Table Tablespaces、General Tablespace 三者中的任意一个地方,具体取决于你的配置和创建表时的 SQL 语句。
Doublewrite Buffer
如果说 Change Buffer 是为了提升性能,那么 Doublewrite Buffer 就是为了保证数据页的可靠性。
前面提到过,MySQL 是以页作为读取和写入的单位,一个页里面有多行数据,在写入数据时,MySQL 会先写内存中的页,然后再刷新到磁盘中的页。
假设在某一次从内存刷新到磁盘的过程中,一个页刷了一半,突然操作系统或者 MySQL 进程奔溃了,这时候,内存里的页数据被清除了,而磁盘里的页数据,刷了一半,处于一个中间状态,可以说是一个不完整甚至是坏掉的的页。
有同学可能会说,不是有 redo log 么?
其实这个时候 redo log 也已经无力回天,因为 redo log 是要在磁盘中的页数据是正常的、没有损坏的情况下,才能把磁盘里的页数据 load 到内存,然后应用 redo log。但如果磁盘中的页数据已经损坏了,这时是无法应用 redo log 的。
所以,MySQL 在刷数据到磁盘之前,要先把数据写到另外一个地方,也就是 Doublewrite Buffer,写完后,再开始写磁盘。
Doublewrite Buffer 可以理解为是一个备份(recovery),万一真的发生 crash,就可以利用 Doublewrite Buffer 来修复磁盘里的数据。