数据页
数据库中的存储结构
记录是按照行来存储的,但是数据库的读取并不以行为单位,在数据库中,不论读一行,还是读多行,都是将这些行所在的页进行加载。也就是说,数据库管理存储空间的基本单位是页(Page)。
一个页中可以存储多个行记录(Row),同时在数据库中,还存在着区(Extent)、段(Segment)和表空间(Tablespace)。行、页、区、段、表空间的关系如下图所示:
页(Page):用来存储数据表达形式行的空间,一页可以存在多个行,每一种数据库或数据引擎对于页的大小定义是不一样的,Sql Server的页大小为8K,、Oralce 支持的块大小为 2KB,4KB,8KB,16KB,32KB 和 64KB、InnoDB为16K。
区(Extent):是比页大一级的我存储结构,在InnoDB引擎中,一个区会分配64个连续的页,不同的引擎上的页默认大小不一致,在InnoDB默认大小为16KB,一个区的大小也就是1M
段(Segment):是由一个区或多个区组成,段并不需要区是连续的,段是数据库的分配单位,创建表的时候会创建一个表段,创建一个索引的时候会创建一个索引段。
表空间(Tablespace):是一个逻辑容器,表空间是由段或多个段组成,对应的关系是一对多的关系,数据库是由一个表空间或多个表空间组成,从管理上可以划分为系统表空间、用户表空间、临时表空间等。
在 InnoDB 中存在两种表空间的类型:共享表空间和独立表空间。如果是共享表空间就意味着多张表共用一个表空间。如果是独立表空间,就意味着每张表有一个独立的表空间,也就是数据和索引信息都会保存在自己的表空间中。独立的表空间可以在不同的数据库之间进行迁移。
数据页的结构
数据库IO的操作的最小单位为页,数据页的结构可以分为三部分
-
1、文件通用部分:文件头和文件尾
文件头:文件头中有两个字段FIL_PAGE_PREV 和 FIL_PAGE_NEXT它们的作用相当于指针,分别指向上一个数据页和下一个数据页,连接起来的页相当于一个双向的链表,采用链表的结构让数据页之间不需要是物理上的连续,而是逻辑上的连续。在页里面,记录之间采用的单向链表的方式
文件尾:文件尾的校验方式就是采用 Hash 算法进行校验,通过文件尾的校验和(checksum 值)与文件头的校验字段值和做比对,如果两个值不相等则证明页的传输有问题,需要重新进行传输,否则认为页的传输已经完成。 -
2、记录部分:
页的主要作用是存储记录,所以“最小和最大记录”和“用户记录”部分占了页结构的主要空间。另外空闲空间是个灵活的部分,当有新的记录插入时,会从空闲空间中进行分配用于存储新记录 -
3、索引部分:
这部分重点指的是页目录,它起到了记录的索引作用,因为在页中,记录是以单向链表的形式进行存储的。单向链表的特点就是插入、删除非常方便,但是检索效率不高,最差的情况下需要遍历链表上的所有节点才能完成检索,因此在页目录中提供了二分查找的方式,用来提高记录的检索效率。
首先针对链表进行数据分组,总共存在三种分组情况第一组、最后一组、其他组,第一组的数量只会有一条记录、最后一组是记录数最大的一组会有1-8条记录,其他组会尽量平均在4-8条之间,每个组的最后一条记录的头部信息会存储着该组的纪录数,而页的页目录空间根据最后一条记录的做地址偏移量,这些地址偏移量被称为槽(slot),每个槽相当于指针指向不同组的最后的一个记录,最终根据槽做二分查找,可以取到我们的内容。
从数据页结构的角度看B+树
数据页的角度看 B+ 树是如何进行查询
一棵 B+ 树中,每个节点都是一个页,每次新建节点的时候,就会申请一个页空间。同一层上的节点之间,通过页的结构构成一个双向的链表(页文件头中的两个指针字段)。非叶子节点,包括了多个索引行,每个索引行里存储索引键和指向下一层页面的页面指针。最后是叶子节点,它存储了关键字和行记录,在节点内部(也就是页结构的内部)记录之间是一个单向的链表,但是对记录进行查找,则可以通过页目录采用二分查找的方式来进行。
B+ 树是如何进行记录检索的
通过 B+ 树的索引查询行记录,首先是从 B+ 树的根开始,逐层检索,直到找到叶子节点,也就是找到对应的数据页为止,将数据页加载到内存中,页目录中的槽(slot)采用二分查找的方式先找到一个粗略的记录分组,然后再在分组中通过链表遍历的方式查找记录。
结构-决定特性
磁盘IO
前提InnoDB的索引本质是B+Tree,对于一棵B+Tree的索引树来说,查找数据是需要从根节点一直找到子节点,而从磁盘的角度上看,就是需要先将数据所在的页读入到内存上,再在内存中对页里面的用户记录空间上进行查找,这样来说尽管你只需要页上面的一条记录,对于磁盘来说,也是需要加载整页的数据,这点很关键。
数据库缓冲池
磁盘IO需要消耗的时间很多,因此为了能让数据表或索引中的数据能随时让我们取用,DBMS会申请占用内存来做为数据的缓冲池,尽量减少磁盘的活动,这种策略对于提升sql的查询性能至关重要。首先我们先看下缓冲池是如何工作的。
服务器发起sql查询,经过引擎的优化器以及执行计划分析后通过查询执行器直接惊险该数据库的访问,在访问的过程中,缓冲池管理器会尽量将经常使用的数据保存起来,在数据库进行页面读操作的时候,首先会判断该页面是否在缓冲池中,如果存在就直接读取,如果不存在,就会通过内存或磁盘将页面存放到缓冲池中再进行读取。
对于缓冲池上的数据的更新,首先会修改缓冲池中页里面的记录信息,然后数据库会以一定的频率刷新到磁盘上。这个处理并不是实时性的,为了数据库的整体性能,缓冲池会采用checkpoint的机制将数据回写到数据库中。当缓冲池不够用时,需要释放掉一些不常用的页,就可以采用强行采用 checkpoint 的方式,将不常用的脏页回写到磁盘上,然后再从缓冲池中将这些页释放掉。这里脏页(dirty page)指的是缓冲池中被修改过的页,与磁盘上的数据页不一致。
缓冲池的一些命令查看
MySQL MyISAM 存储引擎,它只缓存索引,不缓存数据;InnoDB引擎可以通过命令进行查看。
查看缓冲池的大小
mysql > show variables like ‘innodb_buffer_pool_size’
查看缓冲池的个数(大小参数设置为大于等于 1GB,缓冲池个数会大于1个)
mysql > show variables like ‘innodb_buffer_pool_instances’
数据页加载的三种方式
决定性能跟缓冲池的读取方式息息相关
- 内存读取
如果该数据存在于内存中,基本上执行时间在 1ms 左右,效率还是很高的。 - 随机读取
如果数据没有存在内存中,那么需要在磁盘中对页进行查找,单个页的时间预估为10ms左右,其中分为磁盘的寻道和半圈旋转时间、排队时间以及传输时间。面对大批量的情况下还是通过顺序读取的方式来提高整体的性能。 - 顺序读取
顺序读取实质上是一种批量读取的方式,因为我们请求的数据一半多是相邻存储的,这就允许我们使用批量读取页,一次性加载到缓冲池上,不需要单独的磁盘IO操作。如果一个磁盘的吞吐量是 40MB/S,那么对于一个 16KB 大小的页来说,一次可以顺序读取 2560(40MB/16KB)个页,相当于一个页的读取时间为 0.4ms。采用批量读取的方式,即使是从磁盘上进行读取,效率也比从内存中只单独读取一个页的效率要高。
查看查询优化器的成本,在查询语句后执行知道大概需要多少页的查询量
mysql> SHOW STATUS LIKE ‘last_query_cost’;
结构-决定特性
数据库Down掉之后的Recover,只能是用最新对backup+checkpoint+transaction的log 恢复,就是因为commit的内容还没有从缓冲池写入磁盘。
来源:CSDN
作者:临渊御风
链接:https://blog.csdn.net/a18020162344/article/details/103462088