东软集团 Java 面试题,尚硅谷佟刚个人资料,恶补这份“阿里面试宝典”
InnoDB 总体结构
首先我们来看官网的一张图(图片来源于 MySQL 官网):
从上图中可以看出其主要分为两部分结构,一部分为内存中的结构(上图左边),一部分为磁盘中的结构(上图右边)
内存结构
InnoDB 内存中的结构主要分为:Buffer Pool,Change Buffer 和 Log Buffer 三部分。
Buffer Pool
Buffer Pool 是 InnoDB 缓存表和索引的一块主内存区域,Buffer Pool 允许直接从内存中处理经常使用的数据,从而加快处理速度,带来一定的性能提升。 但是缓存总有放满的时候,当缓存满了新来的数据怎么处理呢?Bufer Pool 中采用的是 LRU(least recently used,最近最少使用)算法,LRU 列表中最前面存的是高频使用页,尾部放的是最少使用的页。当有新数据过来而缓存满了就会覆盖尾部数据。
假如我们有一条查询语句非常大,返回的结果集直接就超过了 Buffer Pool 的大小,而这种语句使用场景又是极少的,可能查询这一次之后很久不会查询,而这一次就将缓存占满了,将一些热点数据全部覆盖了。为了避免这种情况发生,InnoDB 对传统的 LRU 算法又做了改进,将 LRU 列表分拆分为 2 个,如下图(图片来源于 MySQL 官网):
该算法在 new 子列表中保留大量页面(5/8),old 子列表包含较少使用的页面(3/8);old 子列表中数据可能会被覆盖,该算法具体操作如下:
3/8 的 Buffer Pool 空间用于 old 子列表
列表的中点是 new 子列表的尾部与 old 子列表的头部之间的边界
当 InnoDB 将一个页面读入缓冲池时,它首先将它插入到中间点(old 子列表的头)。读取的页面是由用户发起的操作(比如 SQL 查询)或 InnoDB 自动执行的预读操作
访问 old 子列表中的页面使其“young”,并将其移动到 new 子列表的头部。如果读取的页是由用户发起的操作,那么就会立即进行第一次访问,并使页面处于 young 状态;如果读取的页是由预读发起的操作,那么第一次访问不会立即发生,而且可能直到覆盖都不会发生。
操作数据时,Buffer Pool 中未被访问的页会逐渐移到尾部,最终会被覆盖。
默认情况下,查询读取的页面会立即移动到新的子列表中,这意味着它们在缓冲池中停留的时间更长。
Change Buffer
Change Buffer 是一种特殊的缓存结构,用来缓存不在 Buffer Pool 中的辅助索引页, 支持 insert, update,delete(DML)操作的缓存(注意,这个在 MySQL5.5 之前叫做 Insert Buffer,仅支持 insert 操作的缓存)。当这些数据页被其他查询加载到 Buffer Pool 后,则会将数据进行 merge 到索引数据叶中。
InnoDB 在进行 DML 操作非聚集非唯一索引时,会先判断要操作的数据页是不是在 Buffer Pool 中,如果不在就会先放到 Change Buffer 进行操作,然后再以一定的频率将数据和辅助索引数据页进行 merge。这时候通常都能将多个操作合并到一次操作,减少了 IO 操作,尤其是辅助索引的操作大部分都是 IO 操作,可以大大提高 DML 性能。
如果 Change Buffer 中存储了大量的数据,那么可能 merge 操作会需要消耗大量时间。
为什么 Change Buffer 只能针对非聚集非唯一索引
因为如果是主键索引或者唯一索引,需要判断数据是否唯一,这时候就需要去索引页中加载数据判断而不能仅仅只操作缓存。
Change Buffer 什么时候会 merge
总体来说,Change Buffer 的 merge 操作发生在以下三种情况:
辅助索引页被读取到 Buffer Pool 时。 当执行一条 select 语句时,会去检查当前数据页是否在 Change Buffer 中,如果在,就会把数据 merge 到索引页
该辅助索引页没有可用空间时。 InnoDB 内部会检测辅助索引页是否还有可用空间(至少有 1/32 页),如果检测到当前操作之后,当前索引页剩余空间不足 1/32 时,会进行一次强制 merge 操作
后台线程 Master Thread 定时 merge。 Master Thread 是一个非常核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性。
Adaptive Hash Index
Adaptive Hash Index,自适应哈希索引。InnoDB 引擎会监控对索引页的查询,如果发现建立哈希索引可以带来性能上的提升,就会建立哈希索引,这种称之为自适应哈希索引,InnoDB 引擎不支持手动创建哈希索引。
Log Buffer
日志缓冲区是存储要写入磁盘日志文件的一块数据内存区域,大小由变量 innodb_log_buffer_size 控制,默认大小为 16MB(5.6 版本是 8MB):
上文讲述 update 语句更新流程一文中,我们只提到了 Buffer Pool 用来代替缓存区,通过本文对内存结构的分析,实际上 Buffer Pool 中严格来说还有 Change Buffer,Log Buffer 和 Adaptive Hash Index 三个部分,DML 操作会缓存在 Change Buffer 区域,而写 redo log 之前会先写入 Log Buffer,所以 Log Buffer 又可以称之为 redo Log Buffer。
Log Buffer 什么时候写入 redo log
一个大的 Log Buffer 空间大允许运行大型事务,而无需在事务提交之前将 redo log 数据写入磁盘。Log Buffer 中的数据会定期刷新到磁盘,那么 Log Buffer 的数据又是如何写入磁盘的呢?Log Buffer 数据 flush 到磁盘有三种方式,通过变量 innodb_flush_log_at_trx_commit 控制,默认为 1。 |value|描述|
当设置为 0 时,由于数据还在内存,所以崩溃后数据基本会被丢失
当设置为 2 时,由于数据已经实时写到 redo log 了,如果磁盘文件没有被损坏,还是可以恢复的
另外,Mast Thread 默认 1s 进行一次刷盘操作,这个可以通过变量 innodb_flush_log_at_timeout 控制,默认 1s。
磁盘结构
InnoDB 引擎的磁盘结构,从大的方面来说可以分为 Tablespace 和 redo log 两部分
Tablespace
Tablespace 可以分为 4 大类,分别是:System Tablespace,File-Per-Table Tablespaces,General Tablespaces,Undo Tablespaces
System Tablespace
系统表空间中包括了 InnoDB data dictionary,doublewrite buffer, change buffer, undo logs 4 个部分,默认情况下 InnoDB 存储引擎有一个共享表空间 ibdata1,如果我们创建表没有指定表空间,则表和索引数据也会存储在这个文件当中,可以通过一个变量控制(后面会介绍)。
ibdata1 文件默认大小为 12MB,可以通过变量 innodb_data_file_path 来控制,改变其大小的最好方式就是设置为自动扩展。
上面表示默认表空间 ibdata1 大小为 12MB,支持自动扩展大小。
当我们的文件达到一定的大小之后,比如达到了 998MB,我们就可以另外开启一个表空间文件:
关于上面的设置有 3 点需要注意:
innodb_data_home_dir 如果不设置的话,那么就默认所有的表空间文件都在 datadir 目录下,而我们上面指定了 2 个不同路径,所以需要把 innodb_data_home_dir 设为空
autoextend 这个属性,只能放在最后一个文件
指定新的表空间文件名的时候,不能和现有表空间文件名一致,否则启动 MySQL 时会报错
当然,表空间可以增大,自然也可以减少,但是一般我们都不会去设置减少,而且减少表空间也相对麻烦,在这里就不展开叙述了。
InnoDB Data Dictionary
InnoDB 数据字典由内部系统表组成,其中包含用于跟踪对象(如表、索引和表列)的元数据。元数据在物理上位于 InnoDB 系统表空间中。由于历史原因,数据字典元数据在某种程度上与存储在 InnoDB 表元数据文件(.frm 文件)中的信息重叠。
Doublewrite Buffer
Doublewrite Buffer,双写缓冲区,这个是 InnoDB 为了实现 double write 而设置的一块缓冲区,double write 和上面的 change buffer 一个确保了可靠性,一个确保了性能的提升,是 InnoDB 中非常重要的两大特性。
我们先来看下面一张图:
InnoDB 默认页的大小是 16KB,而操作系统是 4KB,如果存储引擎正在写入页的数据到磁盘时发生了宕机,可能出现页只写了一部分的情况,比如只写了 4K,这种情况叫做部分写失效(partial page write),可能会导致数据丢失。
可能有人会说,可以通过 redo log 来恢复,但是注意,redo log 恢复数据有一个前提,那就是页没有损坏,如果页本身已经被损坏了,那么是没办法恢复的,所以为了确保万无一失,我们需要先保存一个页的副本,如果出现了上面的极端情况,可以用页的副本结合 redo log 来恢复数据,这就是 double write 技术。
double write 也是由两部分组成,一部分是内存中的 double write buffer,大小为 2MB,另一部分是物理磁盘上的共享表空间中的连续 128 个页,大小也是 2MB,写入流程如下图(图片来源于《MySQL 技术内幕 InnoDB 存储引擎》):
double write 机制会使得数据写入两次磁盘,但是其并不需要两倍的 I/O 开销或两倍的 I/O 操作。通过对操作系统的单个 fsync()调用,数据以一个大的顺序块的形式写入到双写入缓冲区。
在大多数情况下默认启用了 doublewrite 缓冲区。要禁用 doublewrite 缓冲区,可通过将变量 innodb_doublewrite 设置为 0 即可。
最后
既已说到 spring cloud alibaba,那对于整个微服务架构,如果想要进一步地向上提升自己,到底应该掌握哪些核心技能呢?
就个人而言,对于整个微服务架构,像 RPC、Dubbo、Spring Boot、Spring Cloud Alibaba、Docker、kubernetes、Spring Cloud Netflix、Service Mesh 等这些都是最最核心的知识,架构师必经之路!下图,是自绘的微服务架构路线体系大纲,如果有还不知道自己该掌握些啥技术的朋友,可根据小编手绘的大纲进行一个参考。
如果觉得图片不够清晰,也可来找小编分享原件的 xmind 文档!
且除此份微服务体系大纲外,我也有整理与其每个专题核心知识点对应的最强学习笔记:
出神入化——SpringCloudAlibaba.pdf
SpringCloud 微服务架构笔记(一).pdf
SpringCloud 微服务架构笔记(二).pdf
SpringCloud 微服务架构笔记(三).pdf
SpringCloud 微服务架构笔记(四).pdf
Dubbo 框架 RPC 实现原理.pdf
Dubbo 最新全面深度解读.pdf
Spring Boot 学习教程.pdf
SpringBoo 核心宝典.pdf
第一本 Docker 书-完整版.pdf
使用 SpringCloud 和 Docker 实战微服务.pdf
K8S(kubernetes)学习指南.pdf
另外,如果不知道从何下手开始学习呢,小编这边也有对每个微服务的核心知识点手绘了其对应的知识架构体系大纲,不过全是导出的 xmind 文件,全部的源文件也都在此!
评论