关系型数据库 MySQL 之 InnoDB 体系结构
一、InnoDB 体系结构
InnoDB 存储引擎是 MySQL 5.5 版本后的默认存储引擎,支持事务 ACID,回滚,系统崩溃恢复能力及多版本并发控制的事务安全,主要用于 OLTP 数据库业务场景;支持自增长列(auto_increment);支持外键约束(foreign key);支持 MVCC 的行级锁;使用 Btree 索引;如果你还没有看到前面一文介绍 MySQL 体系结构,那么推荐戳此查看[MySQL 体系结构详解],介绍完 MySQL 体系结构,下面来一起学习 InnoDB 体系结构。
下图为 MySQL 5.7 官方手册下 InnoDB 体系结构,详情可见官方手册。
InnoDB存储引擎主要包括多个内存池以及后台线程。
内存池
内存池由 Buffer Pool 包括(undopage、Change buffer page、AdaptiveHash Index、Lock info、datadictionary),Double write Buffer,Additional Memory pool、Redo Log Buffer 组成,主要维护进程/线程的内部数据、缓存磁盘数据,修改文件前先在内存中修改;
MySQL5.7 官方文档中关于 InnoDB 存储引擎的体系结构讲解包括:
Buffer Pool
Change Buffer
Adaptive Hash Index
Redo Log Buffer
System Tablespace
InnoDB DataDictionary
Doublewrite Buffer
Undo Logs
File-Per-TableTablespaces
General Tablespaces
Undo Tablespace
Temporary Tablespace
Redo Log
共享表空间和独立表空间
InnoDB 有表空间的概念,包括共享表空间和独立表空间(独立表空间的模式中也要有系统表空间 ibdata1,来用于存储内部的数据字典、Undo日志等,通过 InnoDB_file_per_table 参数设置启用独立表空间),默认独立表空间模式下会在数据目录下创建 tablename.ibd、tablename.frm 文件,但如果单表增长过快这样就很容易出现性能问题。共享表空间的数据和文件放在一起方便管理,但共享表空间无法在线回收空间,若想要回收需要将全部的 InnoDB 表中的数据备份、删除原表,然后再把数据倒回到原表结构一样的新表中。
临时表空间和通用表空间
MySQL 5.7 把临时表的数据从系统表空间中抽离出来,形成自己的独立的表空间参数innodb_temp_data_file_path, 并且把临时表的相关检索信息保存在系统信息表的 information_schema 库下的 innodb_temp_table_info 表中。但目前还不能定义临时表空间文件的存放路径,只能和 innodb_data_home_dir 一致。临时表空间命名为 ibtmp1,默认大小为 12 M。
通用表空间就是将多个表放置在同一个表空间中,可以根据 活跃度来划分表,存在不同的磁盘上,可减少 metadata 的存储开销,但很少在生产环境中使用。
redo
InnoDB 支持记录 Redo文件,记录对所有页面的修改(页面物理结构的变更)操作,可以通过相关的参数进行自定义设置 Redo文件存储路径。
undo
InnoDB 支持记录 Undo 文件,记录数据修改前的备份,存储在共享表空间中。用户保证事务的原子性(恢复)和实现 MVCC 多版本并发控制,辅助完成事务的持久化(Undo 信息会持久化)。可通过相关参数进行自定义设置。
更改缓冲
更改缓冲(change buffer),基于聚集索引的操作是顺序的,不会造成数据库随机读取,但修改非聚集索引时就会产生大量的随机读写。当修改非聚集索引的数据时,修改操作并非实时更新索引的叶子页,而是把若干对同一页面的更新缓存起来做合并(merge)将一次性更新操作,转化随机 IO 为顺序 IO ,这样可以避免随机 IO 带来性能损耗,提高数据库的写性能。
两次写
两次写(double write),(重做日志记录的是页的物理操作,如果页本身损坏,对其重做就没有意义了,在应用重做日志前,需要一个页的副本。
先通过页的副本还原该页,再应用重做日志进行恢复)当 MySQL 将脏数据 flush 到 data file 的时候,先使用memcopy 将脏数据复制到内存中的double write buffer,之后通过double wirte buffer 再分 2 次,每次写入 1M 到共享表空间,然后马上调用 fsync 函数,同步到磁盘上;
将数据页(page)加载到内存(InnoDB buffer)->更新数据产生脏页(dirty page)->使用 memcopy 将脏数据复制到内存中的double write buffer(size=2M)->double wirte buffer 再分2次,每次写入1 M 到共享表空间(ibdata 文件)->调用 fsync 函数,同步到磁盘;
使用memcopy将脏数据复制到内存中的double write buffer(size=2M)->double wirte buffer 再分 2 次,每次写入 1M 到共享表空间(ibdata 文件)就是 double 的过程。
自适应 hash 索引
自适应 hash 索引(adaptive hash index),InnoDB 会监控表上索引的查找情况,如果通过建立 Hash 索引能带来性能的提升,则会自动建立 hash 索引,该过程只能由MySQL Server 自行控制,无法人工干预且只适用于等值索引查询;从 MySQL 5.7.8 开始,自适应哈希索引搜索系统是分区的,每个索引都会绑定到一个特殊的分区上面,且各个分区都有自己的锁存器来进行保护。
分区可以通过 InnoDB_adaptive_hash_index_parts 参数来控制,该参数的默认值为 8 个,最大可设置为 512。通过设置分区值,可以降低争用,提高并发性。还可以通过命令 SHOW ENGINE INNODB STATUS 所输出的 SEMAPHORES 部分来监控自适应哈希索引的使用及其竞争情况。如果你看到许多线程正在等待一个在 btr0sea.c 中创建的 RW-latch,则它可能被用于禁用自适应哈希索引。
后台线程
后台线程包括(Mater Thread、IOThread、Lock Monitor Thread、ErrorMonitor Thread、Purge Thread、PageCleaner Thread)刷新内存池中的过程数据,管理维护 InnoDB 存储引擎正常工作。
InnoDB 主要的后台线程如下图。
Master Thread 是一个非常核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括:脏页(dirty page)的刷新、合并插入缓冲(insert buffer merge)、回滚页回收(undo purge)等。innodb_max_dirty_pages_pct 脏页刷盘的配置参数,新版默认 75。
二、InnoDB 表逻辑结构
InnoDB 逻辑存储单元主要分为表空间、段、区和页,层级关系为tablespace——>segment——>extent(64 个 page, 1MB )——>page(默认 16K)。
表空间
表空间是 InnoDB 存储引擎逻辑结构的最高层,所有的数据都是存储在表空间中的,表空间又可以分为系统表空间、独立表空间。MySQL 5.7 之后从系统表空间中抽离出来了临时表空间(temporarytablespace)、通用表空间(general tablespace)。
段
段,表空间是由段组成可以把表理解为一个段,又数据段,索引段,回滚段等。每个段又由 N 个区和 32 个零散的页组成,段空间扩展是以区为单位进行扩展的。
区
区,由 64 个连续的页组成,是物理上连续分配的一段空间,每个区固定大小 1MB。
页
页,InnoDB 的最小物理存储分配单位是 page,每个页 16KB 且不能修改,数据页,索引页,系统页等。
行
行,页里面记录着行记录的信息, InnoDB 存储引擎是面向行的,也就是数据是按行存储的,每页最多允许存放 7992 行数据。
行记录格式:
InnoDB 存储引擎有两种文件格式,一种叫 Antelope,另一种叫 Barracuda。在 Antelope 文件格式下,有 compact 和 redundant 两种行记录格式,而在 Barracuda 文件格式下,有 compressed 和 dynamic 两种行记录格式。
对于 Compact,不管是 char 型还是 varcha r型,null 型都是不占用存储空间的;对于Redundant,varchar 的 null 不占用空间,char 的 nul l型是占用存储空间的。
Varchar 类型的长度是 65535 ,但实际一般除开其他开销大概 65530 左右,同时这个限制是一整行数据的长度。
create tabletbl_varchar(
a varchar(22000), b varchar(22000), c varchar(22000)
)
charset=latin1 engine=innodb; #会报错,因为一行总长大于了65535
数据页结构
File Header(文件头):记录页的头信息,固定长度 38 字节;
Page Header(页头):记录页的状态信息,固定长度 65 字节;
Infimum+SupremumRecords:两个虚拟的行记录,用于限定记录的边界;
User Records:用户记录,实际记录的内容,InnoDB 采用 B+ 树索引组织存储表;
Free Space:空闲空间,链表数据结构,记录删除后会被加入空闲空间;
Page Director:页目录,存放记录的相对位置,B+ 索引不能找到具体的一条记录,只能找到该记录所在的页,数据库把页载入内存,再通过 PageDirector 查找具体记录行;
File Trailer:文件结尾信息,固定长度 8 字节;
三、内存结构
MySQL 的内存结构和 Oracle 内存结构相似,也可分为 SGA(系统全局区)和 PGA(系统全局区),数据库的内存参数配置可以使用 [show variables like ‘%buffer%’] 命令查看。
内存主要由以下部分组成,由于篇幅具体每个功能暂时不做介绍,后期有机会在说。
注意:temp_table_size 和 max_heap_table_size 大小设置成一致,如果不一致,则会按照最小的来起作用,但也不要太小,否则会报错。
四、SQL 查询全过程
如下图所示,SQL 查询结果的全过程。
查询缓存,判断 SQL 语句是否完全匹配,再判断是否有权限,两个判断为假则到解析器解析语句,为真则提取数据结果返回给用户;
解析器解析:解析器先词法分析,语法分析,检查错误比如引号有没闭合等,然后生成解析树;
预处理:预处理解决解析器无法决解的语义,如检查表和列是否存在,别名是否有错,生成新的解析树;
优化器做大量的优化操作;生成执行计划;
查询执行引擎,负责调度存储引擎获取相应数据;返回结果。
参考资料
https://my.oschina.net/peakfang/blog/2240253
https://dev.mysql.com/doc/refman/5.7/en/innodb-storage-engine.html
张甦 著 《MySQL王者晋级之路》
注:全文均参考以上资料,如侵权联系可及时删除,若想获取更多,可访问以上链接。
推荐阅读:
关系型数据库 MySQL 体系结构详解
关系型数据库 MySQL 表相关操作
Linux 下 CentOS 7 安装教程
资源分享:
5T 技术资源大放送!包括但不限于:Linux、Python、Oracle、MySQL、Java、前端、大数据、人工智能等,具体获取方式可关注本公众号或者添加我微信获取~~
添加微信,可加入资源技术交流群
长按 识别二维码 即可关注!
走过路过,不要错过这个 好看 哦!