mysql进阶(四)
mysql存储结构:mysql为了方便管理数据,自己定义的一种数据的存储结构。
undo日志(撤销日志,为了事务回滚)和redo日志(保证事务的持久性),这两个日志都是为了保证事务的原子性。
1. InnoDB简介
2. MySQL为什么默认使⽤InnoDB存储引擎?
• InnoDB在设计时考虑到了处理巨⼤数据量时的性能,InnoDB⽀持事务(transaction)、回滚 (rollback)并且具有崩溃修复的能⼒(crashrecoverycapabilities),通过多版本并发控制(multi versioned concurrency control)减少锁定,同时还⽀持外键约束(FOREIGNKEYconstraints),通 过缓冲池在主内存中缓存数据从⽽提⾼查询性能,也可以每个表使⽤各⾃的独⽴表空间存储数据并 且⽂件⼤⼩只受限于操作系统,由于InnoDB存储引擎存储数据量⼤,性能⾼,可以有效的保证数 据安全等优点,在MySQL5.5版本之后成为默认的存储引擎。
3. InnoDB存储引擎的架构⻓啥样?
如下图所示:
下图是官⽅给出的架构图,可以看出其中InnoDB主要包括内存结构(左)和磁盘结构(右):
就是提升性能与安全。
4 使⽤InnoDB存储引擎创建的表对应的数据⽂件在哪⾥?
5. 什么是表空间⽂件
mysql存储结构对应到空间上就是表空间文件。创建表示生成的数据文件存储在表对应的目录下生成一个子目录文件里。
6. ⽤⼾数据在表空间中是怎么存储的?即mysql的存储结构是如何定义的
⽤⼾的数据以数据⾏的⽅式存储在对应的表空间⽂件中,那么表空间中很多个数据 ⾏就需要进⾏管理,以便后续进⾏⾼效的查询;
为了⽅便管理,表空间由段(segment)、区组(group)、区(extent)、⻚(page)、数据⾏组成, 其中⻚是InnoDB磁盘管理的最⼩单位;
若⼲数据⾏组成了⻚,多个⻚组成了区,多区组成了区组,多个区组组成了段,多 个段组成了表空间
7.为什么要使⽤⻚这个数据管理单元
•MySQL中的⻚是应⽤层的⼀个概念,是MySQL根据⾃⾝的应⽤场景,定义的⼀ 种数据结构。
• 通常操作系统中的⽂件系统在管理磁盘⽂件时以4KB⼤⼩为⼀个管理单元,称为"数据块",但是在 数据库的应⽤场景⾥,查询时数据量都⽐较⼤,如果也使⽤4KB做数据存储的最⼩的单元,就显的 有点⼩了,同时会造成频繁的磁盘I/O,导致降低效率;
• 所以MySQL根据⾃⾝情况定义了⼤⼩为16KB的⻚(一次io),做为磁盘管理的最⼩单位;
• 每次内存与磁盘的交互⾄少读取⼀⻚,所以在磁盘中每个⻚内部的地址都是连续的,之所以这样做,是因为在使⽤数据的过程中,根据局部性原理,将来要使⽤的数据⼤概率与当前访问的数据在 空间上是临近的,所以⼀次从磁盘中读取⼀⻚的数据放⼊内存中,当下次查询的数据还在这个⻚中 时就可以从内存中直接读取,从⽽减少磁盘I/O,提⾼性能
这里的资源指的是内存资源。
• MySQL根据⾃⾝的应⽤场景使⽤⻚做为数据管理单元,最主要的⽬的就是减少磁盘I/O,提⾼性 能。
什么是局部性原理?
局部性原理是指程序在执⾏时呈现出局部性规律,在⼀段时间内,整个程序的执⾏仅限于程序中 的某⼀部分。相应地,执⾏所访问的存储空间也局限于某个内存区域,局部性通常有两种形式:时间 局部性和空间局部性。
时间局部性(TemporalLocality):如果⼀个信息项正在被访问,那么在近期它很可能还会被再 次访问。
空间局部性(SpatialLocality):将来要⽤到的信息⼤概率与正在使⽤的信息在空间地址上是临近的。
8. 数据⻚有哪些基本特性是必须要掌握的?
在不同的使⽤场景中,⻚的结构也有所不同,在MySQL中有多种不同类型的⻚,但不论哪种类型的 ⻚都会包含⻚头(FileHeader)和⻚尾(FileTrailer),在这⻚头和⻚尾之间的⻚主体信息根据不同的 类型有不同的结构,最常⽤的就是⽤来存储数据和索引的"索引⻚",也叫做"数据⻚",⻚的主体信 息使⽤数据"⾏"进⾏填充,⾏的结构我们在下⾯的章节中进⾏详细介绍,⻚的基本结构如下图所 ⽰:
9.查询的数据超过⼀⻚的⼤⼩,怎么提⾼查询效率?区
页:磁盘管理的最小单位,内存和磁盘之间交互的基本单位。,每访问一个数据员就会发生一次磁盘io。
磁盘中每个⻚内部的地址都是连续的。
不同的⻚在磁盘中是不是连续的呢?
不⼀定,在不做任何控制的情况下,不同⻚在磁盘中申请的地址⼤概率是不连续的。连续的地址对查询效率是有影响,如果⻚在磁盘中可以被连续读取,那么查 询效率就⾼,否则果询效率就低。
不连续的地址会降低查询的效率
当存储介质是机械硬盘时,访问不连续的地址会带来磁盘寻址的开销,也就是磁头在不同盘⾯、磁 道和扇区的机械转动,这个过程称为磁盘随机访问,⾮常影响效率,磁盘结构如下图所⽰:
当查询的数据⼤于⼀⻚时不加任何控制会产⽣磁盘随机访问,这个是影响查询效 率的主要因素,那么现在怎么提⾼查询效率的问题就变成了,⻚在磁盘中是否连续的问题。
InnoDB如何保证⻚在磁盘中的连续性?
为了解决磁盘随机访问⾮常低效的问题,需要尽可能在磁道上读取连续的数据,减少磁头的移动, 从⽽提升效率,MySQL使⽤Extent(区)这个结构来管理⻚,规定每个区固定⼤⼩为 1MB ,可以存 放 64 个⻚,这时如果跨⻚读数据时,⼤概率都在附近的地址,可以⼤幅减少碰头移动;
同时,如果频繁的读取某个区中的⻚,可以把整个区都读取出来放⼊内存中,减少后续查询对磁盘 的访问次数,进⼀步提升效率,如图所⽰
⼩结:
1. 区的特点
每个区固定⼤⼩为1MB
2. 区和⻚的关系
区是⽤来管理⻚的⼀种数据结构,其中包含若⼲个⻚,从⽽保证⻚与⻚之间的连续性
10.当表中的数据很少时如何避免空间浪费?
• 这些零散⻚会放在表空间中⼀个叫碎⽚区的区域,随着数据量的增加,会申请新的⻚来存储数据, 当碎⽚区达到32个⻚的时候,后续每次都会申请⼀个完整的区来存储更多的数据;
• 通过零散⻚和碎⽚区避免空间浪费的问题
11. 如果访问的数据跨区了怎么办?区组
使⽤区组结构有效的管理区,每个区组固定管理256个区即 区的偏移并⽤双向链表连接。
12. 以上这些数据结构还有优化的空间吗?段
以上讲到的区、区组还有⻚这种都是物理结构
在物理结构的基础上,定义了⼀个逻辑上的概念,也就是"段";
• "段"并不对应表空间中的连续的物理区域,可以看做是"区"和"⻚"的⼀个附加标注信息,段的主 要作⽤是区分不同功能的区和在碎⽚区中的⻚,主要分为"叶⼦节点段"和"⾮叶⼦节点段"等,这两个段和我们常说的B+树索引中的叶⼦、⾮叶⼦节点对应,可以简单的理解为"⾮叶⼦节点段"存储 和管理索引树,"叶⼦节点段"存储和管理实际数据,从逻辑上讲,最终由"叶⼦节点段"和"⾮叶⼦ 节点段"等段构成了表空间 .ibd ⽂件,如下图所⽰:
❤⼩结:
1. 段是⼀个逻辑概念,作⽤是管理不同功能的区和碎⽚区中的⻚。
2. 段主要分为"叶⼦节点段"和"⾮叶⼦节点段",对应索引中的叶⼦、⾮叶⼦节点。
1 上⾯讲的所有操作是在哪⾥进⾏的?
• 所有的数据库操作都是在内存中进⾏的,最终会把修改结果刷回磁盘中对应的⻚中。 2 查询数据时MySQL会⼀次把表空间中的数据全部加载到内存吗?
• 当然不是,使⽤InnoDB存储引擎创建表,在查询数据时会根据表空间内部定义的数据结构(⼀般为 索引),定位到⽬标数据⾏所在的⻚,只把符合查询要求的⻚加载到内存。
3 每查询⼀条数据都要进⾏⼀次磁盘I/O吗?
• 不⼀定,每次查询都会把磁盘中数据⾏对应的数据⻚加载到内存中,如果当前查询的数据⾏已经在内存中,则直接从内存中返回结果,从⽽提⾼查询效率。
13. ⻚结构
⻚的⼤⼩可以设置吗?
⻚都有哪些分类?我们需要重点学习哪种⻚?
InnoDB在不同的使⽤场景定义多种不同类型的⻚,常⽤的有 数据⻚、 Undo Log ⻚ 、 Change Buffer ⻚ 、 Extent Descriptor(XDES)⻚、 InnoDB 段信息⻚等,每种⻚的 数据结构都不相同,其中最需要我们关注的就是数据⻚,由于InnoDB中有个概念叫"索引即数据",所以也叫做索引⻚。
• 不论哪种类型的⻚都具有⻚头(FileHeader)和⻚尾(FileTrailer)两个信息
14 ⻚头和⻚尾具体包含了哪些信息
⻚头和⻚尾中包含的是⽤来描述⽂件相关的信息,如下图所⽰:
LSN:是"LogSequenceNumber"的缩写,表⽰⽇志序号。⽤⼀个任意的、不断增加的值表⽰⽇志中 记录的操作对应的时间点,⽤8字节的⽆符号⻓整形表⽰
除了⻚头和⻚尾,数据⻚中还有哪些信息:
• ⻚头和⻚尾中的各个字段描述了当前⻚的类型以及在⽂件系统中的位置,也就是说通过⻚头可以找 到对应的⻚。数据⻚的主要功能是保存数据,在⼀个数据⻚中,除了⻚头与⻚尾占⽤的46个字节之 外的空间都⽤来存储真正的数据,也就是数据⾏,数据⾏会与表⾥的数据⾏⼀⼀对应,基于这⼀特性MySQL也被称为 "⾏式数据库 " ,也可以把除了⻚头⻚尾的区域称为⻚主体。
15 数据⾏有哪些信息组成?
数据⾏可以划分为两部分,⼀部分存储额外信息,⼀部分存储真实数据
额外信息部分包含变⻓字段⻓度列表和NULL值列表两个⼤⼩不确定的区域,以及固定占5字节的头信息区域
数据⾏是如何组织在⼀起的?
• 数据⾏通过下⼀⾏的地址偏移量,即 next_record 将⻚内所有数据⾏组成了⼀个单向链表,地址偏移量指向的是下⼀⾏中真实数据的起始地址,这样做的好处是,向右是真实 数据,向左就是头信息,⽽⽆需额外的⻓度计算,如图所⽰:
当向⼀个新⻚插⼊数据时是如何执⾏的?
16 如果要查询的数据在某⼀个⻚中,如何定位它在⻚中的位置,⼀条条遍历吗?
从头开始遍历是⼀个最简单的⽅法,也可以实现数据的查找,当按主键或索引查找某条数据时,从 头⾏ infimun 开始,沿着链表顺序逐个⽐对查找,但⼀个⻚有16KB,通常会存在数百⾏数据, 每次都要遍历数百⾏,⽆法满⾜⾼效查询。
为了提⾼查询效率,InnoDB采⽤⼆分查找来解决查询效率问题。
• 具体实现⽅式是,在每⼀个⻚中加⼊⼀个叫做⻚⽬录 Page Directory 的结构,将⻚内包括头 ⾏、尾⾏在内的所有⾏进⾏分组,约定头⾏单独为⼀组,其他每个组最多8条数据,同时把每个组 最后⼀⾏在⻚中的地址,按主键从⼩到⼤的顺序记录在⻚⽬录中在,⻚⽬录中的每⼀个位置称为⼀ 个槽,每个槽都对应了⼀个分组,这样在插⼊数据⾏完成链接后,⼀旦最后⼀个分组中的数据⾏超 过分组的上限8个时,就会分裂出⼀个新的分组,为了快速判断每个分组是否达到了8个的上限,在 每个分组最后⼀⾏中⽤ n_owned 记录了这个分组内的⾏数,与此同时在⻚⽬录中创建⼀个新的 槽,后续插⼊的⾏都遵守这个规则;
• 后续在查询某⾏时,就可以通过⼆分查找,先找到对应的槽,然后在槽内最多8个数据⾏中进⾏遍 历即可,从⽽⼤幅提⾼了查询效率;
• 例如要查找主键为6的⾏,先⽐对槽中记录的主键值,定位到最后⼀个槽2,再从最后⼀个槽中的第 ⼀条记录遍历,第⼆条记录就是我们要查询的⽬标⾏。
如上图所示,详细描述一下如何查找索引为6的行数据。
定义两个指针,prev和cur;
首先将两个指针指向页目录中槽0所记录的第一个组的位置;
移动cur指针到槽1记录的位置,发现当前行的索引为4,所以将prev指针也移动到当前cur所指的行位置。
移动cur到cao2所指的位置,发现已经是最后一一行了,所以cur指针不变,移动prev指针,指向prev->4到prev->5,就这样慢慢的移动prev,知道prev指向行索引为6的位置。
17. 关于事务、索引这些信息在⻚中怎么记录?
关于数据页所管理的一次数据信息需要被记录下来。
下图是数据⻚的完整结构,以及所占的磁盘空间 :
18. ⾏结构
• 真实的数据在表空间以数据⾏的形式存储,也就是说每⼀条数据都对应着表中的⼀⾏,数据⾏在⻚中的位置如下图所⽰:
InnoDB⽀持四种⾏格式,不同的⾏格式数据的存储上有所不同 。每一个dynamic数据行都可以划分为两部分。
InnoDB⽀持四种⾏格式,分别是: COMPACT 紧凑格式, REDUNDANT 冗余格式, COMPRESSED 压缩格式,DYNAMIC 动态格式,默认是 DYNAMIC 格式。
# 查看系统变量中设置的⾏格式(全局级别)
# 使⽤ SHOW table STATUS 查看数据库中的所有表
⼀个 DYNAMIC格式的数据⾏会被分为两部分,⼀个部是存储真实数据的区域,⼀部分是存储额外信息的区域。
18. 数据区是怎么存储真实数据的?
接下来就是除了主键和值为NULL的列之外,其他列的真实数据,按照顺序从左到右依次排列 • ⾄于为什么不存储NULL值,原因很简单,就是为了节少空间,所有允许为NULL的列都会在⾏额外 信息区的NULL值列表中进⾏标识,后⾯我们会详细详解,以上就是数据⾏对真实数据的存储⽅式。
19. 额外(管理)信息区包含了关于⾏的哪些信息?
额外信息区从右向左分别为:头信息,Null值列表,变⻓字段列表。
删除⼀⾏记录时在InnoDB内部执⾏了哪些操作?
ps:本文只是用来自己复习时整理的笔记!!!