写点什么

20 道高频面试题(含答案),看完豁然开朗

发布于: 2 小时前

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):


SHOW VARIABLES LIKE 'innodb_log_buffer_size';-- global级别,无session级别
复制代码


上文讲述 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。


SHOW VARIABLES LIKE 'innodb_flush_log_at_timeout';-- global级别,无session级别
复制代码

磁盘结构

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 来控制,改变其大小的最好方式就是设置为自动扩展。


innodb_data_file_path=ibdata1:12M:autoextend
复制代码


上面表示默认表空间 ibdata1 大小为 12MB,支持自动扩展大小。


当我们的文件达到一定的大小之后,比如达到了 998MB,我们就可以另外开启一个表空间文件:


innodb_data_home_dir=innodb_data_file_path=/ibdata/ibdata1:988M;/disk2/ibdata2:50M:autoextend
复制代码


关于上面的设置有 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 即可。

总结

以上是字节二面的一些问题,面完之后其实挺后悔的,没有提前把各个知识点都复习到位。现在重新好好复习手上的面试大全资料(含 JAVA、MySQL、算法、Redis、JVM、架构、中间件、RabbitMQ、设计模式、Spring 等),现在起闭关修炼半个月,争取早日上岸!!!!


下面给大家分享下我的面试大全资料,如果你也有需要,可以戳这里即可免费领取我的这份复习资料


  • 第一份是我的后端 JAVA 面试大全



后端 JAVA 面试大全


  • 第二份是 MySQL+Redis 学习笔记+算法+JVM+JAVA 核心知识整理



MySQL+Redis 学习笔记算法+JVM+JAVA 核心知识整理


  • 第三份是 Spring 全家桶资料



MySQL+Redis 学习笔记算法+JVM+JAVA 核心知识整理

用户头像

还未添加个人签名 2021.07.29 加入

还未添加个人简介

评论

发布
暂无评论
20道高频面试题(含答案),看完豁然开朗