目录
回顾MySQL架构
InnoDB简介
MySQL存储结构
回顾MySQL架构
对MySQL架构图的总结:
MySQL服务器是以网络服务的方式对外提供数据库服务的,我们使用的应用程序以及客户端统称为外部程序。
外部程序通过发送网络请求的方式来连接MySQL服务器,这时首先每一个连接都会到MySQL架构中的连接层进行身份校验,比如用户名和密码是否正确,还会进行一些关于服务器自身连接限制的校验等。
通过校验后,这个请求会被转发到服务层,由具体的执行线程来处理用户的一些交互,如发送一些sql语句,这些sql语句会通过SQL interface来接收,在接收后服务层会对这些文本形式的sql语句进行解析,把关键字等解析出来,如对于一个查询,解析出来表,字段以及where条件等,在解析工作完成后,mysql会把这些sql语句做一些优化,优化为它认为效率最高的形式,最终执行一些预操作,预查询,形成一些sql指令
这些指令会被发送到存储引擎上,在存储引擎中做一些处理。对于存储引擎层是有很多种不同的存储引擎,每一种存储引擎都有自己的特性,可以根据存储引擎的特性以及结合应用程序的具体使用场景来选择适合应用程序的存储引擎,帮助我们处理数据,其中有些数据是要实际落盘的,有些是要在内存中存储的,有些是以文本的形式来进行保存的,有些以B+树来组织的等等
当要真正落盘时,就会与文件系统层进行交互,把要落盘的数据按照自己组织数据的方式写到磁盘文件中进行保存,如Data目录中的表空间文件中
对于查询,存储引擎层也会通过一个检索的过程从磁盘文件中把数据检索出来,然后再逐层向上返回,最终返回给客户端,就完成一个整个数据库的交互
在MySQL架构中核心的层有服务层和存储引擎层,服务层主要完成与用户的交互,接受用户的请求,存储引擎层则是主要完成处理数据,组织数据
在平时使用数据库时,真正处理数据,组织数据,保证查询效率,保证数据安全,保证数据落盘时使用到的都是存储引擎
对于下面内容的介绍都是通过这6个方面进行介绍的,以达到
以问题的形式引导,通过分析如何解决问题,从⽽理解MySQL的实现过程分析对于解决问题的过程中引发的新问题,如何处理或规避
InnoDB简介
1.InnoDB存储引擎的由来
解答问题:
InnoDB做为MySQL的默认存储引擎,其实并不是MySQL开发的,⽽是由 Innobase Oy 公司所开发,2006年五⽉时由甲⻣⽂公司并购。这也是MySQL做为⼀个开源数据库的好处之⼀,第三⽅开发者可以根据⾃⼰的业务场景开发出⾼性能的存储引擎,像 Innobase Oy 公司开发的InnoDB,⼀旦被官⽅采纳,那么就可能通过被收购从⽽得到可观的收⼊,官⽅的产品也得到了有效的扩展,是⼀个双赢的结果。开源社区有很多,除了MySQL之外,常⻅的还有Linux,Java,Android,开发都通过社区交流并贡献⾃⼰的技术,逐步完善各种应⽤场景下的技术⽀持,现在开发过程中使⽤的很多框架和中间件都得益与此
2. MySQL为什么默认使⽤InnoDB存储引擎?
分析过程:这个问题也可以说是InnoDB存储引擎的优点或者InnoDB与MyISAM的区别
解答问题:
InnoDB在设计时考虑到了处理巨⼤数据量时的性能,InnoDB⽀持事务(transaction)、回滚(rollback)并且具有崩溃修复的能⼒(crash recovery capabilities),通过多版本并发控制(multi-versioned concurrency control)减少锁定,同时还⽀持外键约束(FOREIGN KEY constraints),通过缓冲池在主内存中缓存数据从⽽提⾼查询性能,也可以每个表使⽤各⾃的独⽴表空间存储数据并且⽂件⼤⼩只受限于操作系统,由于InnoDB存储引擎存储数据量⼤,性能⾼,可以有效的保证数据安全等优点,在MySQL5.5版本之后成为默认的存储引擎
衍生问题:
1. InnoDB事务是什么?2. 事务的回滚是什么?3. InnoDB崩溃修复如何处理?4. 多版本并发控制是什么?5. 锁分类都有哪些?6. 缓冲池的作⽤及⼯作原理?7. 独⽴表空间的作⽤及优点?将在后面的内容中进行介绍
3.InnoDB存储引擎的架构⻓啥样?
分析过程:这里可以参考官网的介绍
传送门: InnoDB存储引擎架构链接
解答问题:
InnoDB主要包括内存结构和磁盘结构。
衍生问题:
1.为什么要设计成内存结构和磁盘结构两个部分?
我们从MySQL实现的⻆度来思考这个问题,数据库的作⽤就是保存数据,⽤⼾的真实数据最终都会保存在磁盘上,在查询数据的过程中,如果每次都从磁盘上读取会严重影响效率,为了提⾼数据的访问效率,InnoDB会把查询到的数据缓存到内存中,当再次查询时,如果⽬标数据已经存在于内存中,就可以从内存中直接读取,从⽽⼤幅提升效率。也就是说磁盘结构中的⽂件是⽤来保存数据实现数据持久化的,内存结构是⽤来缓存数据提升效率的。
4.使⽤InnoDB存储引擎创建的表对应的数据⽂件在哪⾥?
前置知识:当创建一个数据库时,会在数据目录(工作目录)生成一个与数据库同名的子目录,里面包含创建的表的数据文件
分析过程:⽆论使⽤哪个存储引擎创建表,真实的数据都必须存储在磁盘或其他存储介质中
解答问题:当使⽤InnoDB存储引擎创建⼀个表时,默认会在数据⽬录对应的数据库⼦⽬录中⽣成相应的表空间⽂件(以表名为文件名),以 .ibd 为⽂件的后缀,⽤来存储数据和索引,如果每个表都对应⼀个表空间⽂件,称为独⽴表空间,在MySQL5.7及以后的版本中默认为每个表⽣成独⽴表空间,可以通过系统变量 innodb_file_per_table[=ON|OFF] 进⾏控制,如果关闭这个选项,则所有表的数据都在系统表空间中存储独⽴表空间⽂件如下所⽰:
MySQL存储结构
对于MySQL存储结构可以理解为MySQL为了有效的维护数据而定义的一系列数据结构
1.什么是表空间⽂件?
解答问题:
表空间⽂件是⽤来存储表中数据的⽂件,表空间⽂件的⼤⼩由存储的数据(业务数据)多少决定,不同的表空间⽂件存储数据的种类也有所不同,在MySQL中表空间分为五类,包括:系统表空间、独⽴表空间、通⽤表空间、临时表空间和撤销表空间,这些在上⾯的InnoDB架构图中都有体现。衍⽣问题1.表空间与表空间⽂件的关系是什么?表空间可以理解为MYSQL为了管理数据⽽设计的⼀种数据结构,主要描述的对结构的定义,表空间⽂件是对定义的具体实现,以⽂件的形式存在于磁盘上
2.⽤⼾数据在表空间中是怎么存储的?
分析过程:
(1)⾸先明确⼀点,⽤⼾的数据以数据⾏的⽅式存储在对应的表空间⽂件中,那么表空间中很多个数据⾏就需要进⾏管理,以便后续进⾏⾼效的查询;(2)为了⽅便管理,表空间由段 (segment)、区组(group)、区 (extent)、⻚ (page) 、数据⾏组成,其中⻚是 InnoDB 磁盘管理的最⼩单位;
为了更加方便理解段,区组,区,页,数据行,可以举一个小区中不同层次的概念
解答问题:
可以这么理解,若⼲数据⾏组成了⻚,多个⻚组成了区,多区组成了区组,多个区组组成了段,多个段组成了表空间衍⽣问题1.段 (segment)、区组(group)、区 (extent)、⻚ (page) 、数据⾏是如何管理起来的?后面会详细介绍每个部分的作⽤,他们之间的关系和管理⽅式如下。
3.为什么要使⽤⻚这个数据管理单元?
分析过程:
(1)⾸先要明确⼀点,MySQL中的⻚是应⽤层的⼀个概念,是MySQL根据⾃⾝的应⽤场景,定义的⼀种数据结构。(2)通常操作系统中的⽂件系统在管理磁盘⽂件时以4KB⼤⼩为⼀个管理单元,称为"数据块",但是在数据库的应⽤场景⾥,查询时数据量都⽐较⼤,如果也使⽤4KB做数据存储的最⼩的单元,就显的有点⼩了,同时会造成频繁的磁盘I/O,导致降低效率;(3)MySQL根据⾃⾝情况定义了⼤⼩为16KB的⻚,做为磁盘管理的最⼩单位,一次I/O读16KB,即磁盘与内存交互时,一次交互的为一页,一页多行数据(4)如果实际上只需要查询一条数据,但是从磁盘上读取了16KB,会不会造成过度读取或者资源浪费?,实际上不会,原因为如下的局部性原理每次内存与磁盘的交互⾄少读取⼀⻚,所以在磁盘中每个⻚内部的地址都是连续的,之所以这样做,是因为在使⽤数据的过程中,根据局部性原理,将来要使⽤的数据⼤概率与当前访问的数据在空间上是临近的,所以⼀次从磁盘中读取⼀⻚的数据放⼊内存中,当下次查询的数据还在这个⻚中时就可以从内存中直接读取,从⽽减少磁盘I/O,提⾼性能解答问题:MySQL根据⾃⾝的应⽤场景使⽤⻚做为数据管理单元,最主要的⽬的就是减少磁盘I/O,提⾼性能。衍生问题:什么是局部性原理?局部性原理是指程序在执⾏时呈现出局部性规律,在⼀段时间内,整个程序的执⾏仅限于程序中的某⼀部分。相应地,执⾏所访问的存储空间也局限于某个内存区域,局部性通常有两种形式:时间局部性和空间局部性。时间局部性(Temporal Locality):如果⼀个信息项正在被访问,那么在近期它很可能还会被再次访问。空间局部性(Spatial Locality):将来要⽤到的信息⼤概率与正在使⽤的信息在空间地址上是临近的
4. 数据⻚有哪些基本特性是必须要掌握的? - ⻚
解答问题:
(1)⻚的16KB的⼤⼩是MySQL的⼀个默认设置,可以适⽤于⼤多数场景,当然也可以根据⾃⼰的实际业务场景(根据核心表中的数据行大小)进⾏修改⻚的⼤⼩,通过系统变量 innodb_page_size 进⾏调整与查看,在调整⻚⼤⼩的时候需要保证设置的值是操作系统"数据块" 4KB的整数倍,从⽽保证通过操作系统和磁盘交互时"数据块"的完整性,不被分割或浪费,所以规定了 innodb_page_size 可以设置的值,分别是 4096 、 8192 、 16384 、 32768 、 65536 ,对应 4KB 、 8KB 、 16KB 、 32KB 、 64KB ;(2) 每⼀个⻚中即使没有数据也会使⽤ 16KB 的存储空间,即当数据页写满后,若要创建新的数据页,就会一次性在磁盘上申请一个连续的空间,大小为16KB,当有数据写入表中时,就会找到空闲的或当前使用的页进行填充数据,填满了就再去申请,以此类推同时与索引的B+树中的节点对应,后续会 详细讲解B+树的内容,查看⻚的⼤⼩,可以通过系统变量 innodb_page_size 查看
mysql> SHOW VARIABLES LIKE 'innodb_page_size';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| innodb_page_size | 16384 | # 16KB
+------------------+-------+
1 row in set, 1 warning (0.04 sec)
(3) 在不同的使⽤场景中,⻚的结构也有所不同,在MySQL中有多种不同类型的⻚,但不论哪种类型的⻚都会包含⻚头(File Header)和⻚尾(File Trailer),在这⻚头和⻚尾之间的⻚主体信息根据不同的类型有不同的结构,最常⽤的就是⽤来存储数据和索引的"索引⻚",也叫做"数据⻚",⻚的主体信息使⽤数据"⾏"进⾏填充,⻚的基本结构如下图所⽰:
5.查询的数据超过⼀⻚的⼤⼩,怎么提⾼查询效率 ?区
前置知识:
由上面的内容得知,磁盘中每个⻚内部的地址都是连续的,并且页是内存与磁盘之间交互的最小单位,每访问一个数据页就会发生一次磁盘IO,那么当进行范围查询/where条件中进行or查询时,就有可能访问多个数据页,发生多次磁盘IO
分析过程:(1) 不同的⻚在磁盘中是不是连续的呢?答案是不⼀定,在不做任何控制的情况下,是一种随机的形式,这就导致不同⻚在磁盘中申请的地址⼤概率是不连续的。我们可以很快的分析出来连续的地址对查询效率的影响,如果⻚在磁盘中可以被连续读取,那么查询效率就⾼,否则果询效率就低。(2)为什么不连续的地址会降低查询的效率?当存储介质是机械硬盘时,访问不连续的地址会带来磁盘寻址的开销,也就是磁头在不同盘⾯、磁道和扇区的机械转动,这个过程称为磁盘随机访问,⾮常影响效率,磁盘结构如下:
对磁盘结构的简要介绍
磁盘面是存储数据的,多个磁盘面通过转轴连接在一起,转轴以固定的方式旋转从而带动所有的磁盘面进行旋转,同时机械臂杆为每个磁盘面配置了一个读写磁头,通过磁头就可以把盘面上的数据读到缓冲区,让操作系统读走
经过以上的分析,当查询的数据⼤于⼀⻚时不加任何控制会产⽣磁盘随机访问,这个是影响查询效率的主要因素,那么现在怎么提⾼查询效率的问题就变成了,⻚在磁盘中是否连续的问题(3)InnoDB如何保证⻚在磁盘中的连续性?为了解决磁盘随机访问⾮常低效的问题,需要尽可能在磁道上读取连续的数据,减少磁头的移动,从⽽提升效率,MySQL使⽤ Extent(区) 这个结构来管理⻚,规定每个区固定⼤⼩为 1MB ,可以存放 64 个⻚,这时如果跨⻚读数据时,⼤概率都在附近的地址,可以⼤幅减少磁头移动同时,如果频繁的读取某个区中的⻚,可以把整个区都读取出来放⼊内存中,减少后续查询对磁盘的访问次数,进⼀步提升效率
解答问题:
通过对问题的分析,我们了解到InnoDB中⽤来组织⻚的数据结构--区,并且每个区固定⼤⼩为 1MB ,可以包含64个连续的⻚,查询的数据超过⼀⻚⼤⼩时,可能会有以下⼏种情况:a. ⻚在区内是相邻的:磁盘顺序I/O,可以⼤幅提升效率b. ⻚在区内但不是相邻的:可以⼤幅减少碰头移动,可以提升效率c. ⻚在不同的区:还是要发⽣随机I/O,不能提升效率衍⽣问题:
那么⼜有⼀个问题来了,新创建表时没有数据,或者说有的表只有很少的数据,1MB的空间⽤不完,那不是就存在空间浪费的问题吗?的确是这样,InnoDB在设计时也考虑到了这个问题,我们继续提出问题,然后再来解决。总结:1.区的特点:每个区固定⼤⼩为 1MB2. 区和⻚的关系区是⽤来管理⻚的⼀种数据结构,其中包含若⼲个⻚,从⽽保证⻚与⻚之间的连续性
6.当表中的数据很少时如何避免空间浪费?
分析过程:1.当创建表时,并不知道当前表的数据量级2.为了节省空间,最初只创建7个初始⻚(在MySQL5.7中创建6个初始⻚),⽽不是⼀个完整的区,可以通过以下SQL查看:
mysql> select * FROM information_schema.INNODB_TABLESPACES WHERE name =
'test_db/student'\G
*************************** 1. row ***************************
SPACE: 13
NAME: test_db/student
FLAG: 16417
ROW_FORMAT: Dynamic
PAGE_SIZE: 16384 # ⻚⼤⼩
ZIP_PAGE_SIZE: 0
SPACE_TYPE: Single
FS_BLOCK_SIZE: 65536
FILE_SIZE: 114688 # 数据⽂件初始⼤⼩
ALLOCATED_SIZE: 114688
AUTOEXTEND_SIZE: 0
SERVER_VERSION: 8.0.33
SPACE_VERSION: 1
ENCRYPTION: N
STATE: normal
1 row in set (0.01 sec)
# 根据数据⽂件⼤⼩和每⻚⼤⼩计算出⻚数
# 114688 / 16384 = 7 个数据⻚
3. 这些零散⻚会放在表空间中⼀个叫碎⽚区的区域,随着数据量的增加,会申请新的⻚来存储数据,当碎⽚区达到32个⻚的时候,后续每次都会申请⼀个完整的区来存储更多的数据;
解答问题:通过零散⻚和碎⽚区避免空间浪费的问题
7.如果访问的数据跨区了怎么办?--区组
分析过程:
1.当创建一个区时,要把这个区对应的自定义属性记录下来,包含区的偏移量,区管理数据的范围,再用双向链表把区组织起来
2.不同的区在磁盘上⼤概率是不连续的,那么这个问题其实是InnoDB如何⾼效的的管理区?
当表中的数据越来越多,为了有效的管理区,定义了区组的结构,每个区组固定管理256个区即 256MB ,通过区组可以在物理结构层⾯⾮常⾼效的管理和定位到每个区,对于区组也需要管理自身属性,包含区组管理数据的范围,然后再用双向链表把区组也组织起来
3.第⼀个区组中的⾸个区的前四⻚⽐特殊,也就是初始⻚中的前4⻚,分别是:
File Space Header: 表空间和区组中条⽬信息Insert Buffer Bitmap:Change Buffer相关信息File Segment inode: 段信息B-tree Node:索引根信息其他为空闲⻚⽤来存储真实的数据4.其他区组中⾸个区的结构都⼀样,前两个⻚分别是:Extent Descriptor(XDES):区组条⽬信息Insert Buffer Bitmap:Change Buffer相关信息解答问题:使⽤区组结构有效的管理区,每个区组固定管理256个区即 256MB ,区组条⽬信息中会记录每个区的偏移并⽤双向链表连接衍生问题:如何定位要查询的⽬标⻚?----后续内容说明
8.以上这些数据结构还有优化的空间吗?段
分析过程:
1.以上讲到的区、区组还有⻚这种都是物理结构,在磁盘上有对应的空间大小
2.在物理结构的基础上,定义了⼀个逻辑上的概念,也就是"段"
"段"并不对应表空间中的连续的物理区域,可以看做是 "区" 和 "⻚" 的⼀个附加标注信息,段的主要作⽤是区分不同功能的区和在碎⽚区中的⻚,主要分为"叶⼦节点段"和"⾮叶⼦节点段"等,这两个段和我们常说的B+树索引中的叶⼦、⾮叶⼦节点对应,可以简单的理解为"⾮叶⼦节点段" 存储和管理索引树,"叶⼦节点段"存储和管理实际数据,从逻辑上讲,最终由 "叶⼦节点段" 和 "⾮叶⼦节点段" 等段构成了表空间 .ibd ⽂件,如下图所⽰
解答问题 :
InnoDB使⽤"段"这个逻辑结构区分不同功能的区和在碎⽚区中的⻚,并按功能分为"叶⼦节点段"和"⾮叶⼦节点段",做为B+树索引中的叶⼦、⾮叶⼦节点,从⽽进⼀步提升查询效率以上就是InnoDB存储引擎管理数据时使⽤的⼀些数据结构衍⽣问题:1. 上⾯讲的所有操作是在哪⾥进⾏的?所有的数据库操作都是在内存中进⾏的,最终会把修改结果刷回磁盘中对应的⻚中2.查询数据时MySQL会⼀次把表空间中的数据全部加载到内存吗?当然不是,使⽤InnoDB存储引擎创建表,在查询数据时会根据表空间内部定义的数据结构(⼀般为索引),定位到⽬标数据⾏所在的⻚,只把符合查询要求的⻚加载到内存3.每查询⼀条数据都要进⾏⼀次磁盘I/O吗?不⼀定,每次查询都会把磁盘中数据⾏对应的数据⻚加载到内存中,如果当前查询的数据⾏已经在内存中,则直接从内存中返回结果,从⽽提⾼查询效率总结:1.段是⼀个逻辑概念,作⽤是管理不同功能的区和碎⽚区中的⻚。2. 段主要分为"叶⼦节点段"和"⾮叶⼦节点段",对应索引中的叶⼦、⾮叶⼦节点。
以上数据结构之间的联系图