mysql——页

1.概念

  • 因为 IO操作是很耗费性能的,为了避免一条一条读取磁盘数据,InnoDB采取页的方式,作为磁盘和内存之间交互的基本单位。查询一条数据的时候会把该条数据所在的页全部加载进内存里,以便提高后续查询效率。(存储数据的时候一定会根据主键进行排序,所以id为1,2,3,4,5的数据会在同一页中。)
  • 一个页的大小一般是16KB。
  • InnoDB为了不同的目的而设计了多种不同类型的页,比如:存放表空间头部信息的页、存
    放undo日志信息的页等等。我们把存放表中数据记录的页,称为索引页or数据页。我们主要关注的是索引页(数据页)。

2.页的结构

页的结构

         如图所示,页主要包括File Header、Page Header、Infimum+Supremum、User Records、Free Space、Page Directory以及File Trailer

其中:

        File Header:文件头 存储页的通用信息 页跟页之间的双向关联,指针等

        Page Header:页头 存储数据页专有的信息 

        Infimum+Supremum:页中的最小和最大记录 表中的数据都会最小记录大,比最大记录小,相当于数学中的-∞、+∞的概念。

        User Records:用户记录 存放用户记录,因为最小最大记录不是用户主动去存储的,所以为了方便管理,把最小和最大记录单独进行管理,

        Free Space:空闲空间 与User Records是互斥的关系,主要用来记录当前页的剩余空间大小

        Page Directory:目录,存储记录在页中的相对位置,为了加快查找,能够查询到记录所在的组

        File Trailer:文件尾 校验页是否完整

3.页中存储数据的流程

页中存储数据的流程

  •  开始的时候是没有User Records的,当插入数据的时候,会向Free Space申请空间
  • 插入数据,此时User Records会逐渐变大,而Free Space会逐渐变小
  • 当数据正好满了的时候,Free Space会变为0,不过一般情况下,不会这么凑巧,比如,新插入记录大小为30B,而Free Space只有10B,此时会重新申请一个页存储该条记录,这个10B的Free Space就会剩下在当前页中。

4.记录的头信息

  • 我们在mysql中最常用的行存储格式是compact 行格式

记录的真实数据(insert语句中插入的内容):

  • 主键列的值:存放主键的值
  • 两个隐藏列:
    • trx_id列的值:事务id
    • roll_pointer列的值:回滚的指针,指向回滚的内容 
  • a列/b列的值:除主键外依次插入值

记录的额外信息:

  • 变长字段长度列表:变长字段指varchar等字符串类型,在存储的过程中,int,number都有固定大小的存储,字符串类型传入值多大就会存储多少,这样的目的是节省空间
  • NULL值列表:储存空值列表
  • 记录头信息:(最重要)
    • 预留位1:无需关心
    • 预留位2:无需关心
    • deleted_flag:删除标志,0未删除,1已删除
    • min_rec_flag:目录项,只会在B+树的非叶子节点最小的目录项中添加这个标记

                目录项的作用是快速找到对应的页,在record_type的类型1中使用。

    • n_owned:记录当前组里的数量,此位置只会在主键最大的记录中维护
    • heap_no:堆号,在页面堆(innodb中数据连续排列,这种结构称为heap)中,记录的相对位置,根据heap_no可以直接找到该条记录所在的位置,序号从2开始排列,heap_no为0的是Infimum,为1的是Supremum,分配之后该值就不会发生变化(特别是删除中)。
    • record_type:记录类型,4种类型:
      • 0- 普通记录(用户插入的记录)
      • 1- B+树中非叶子节点的目录项记录(根据目录项可快速找到对应的记录)
      • 2- Infimum
      • 3- Supremum
    • next_record:记录的是当前记录的next_record与下一条记录的next_record之间的偏移量 。 指向下一个记录,记录下一个记录的信息,此处需要注意的是,这里指向的是记录的真实数据的主键列的值,而不是变长字段长度列表,因为这样方便查找,想查下一条记录的额外信息只需要指向下一条记录再往前查询即可,查询真实信息,往后查询即可

5.删除记录操作对next_record的影响

        记录在页中是根据主键从小到大顺序排列的

        记录之间是单向链表,优势是存储空间小,缺点是不能往前遍历

删除记录操作对next_record的影响

 由上图可以看出:

  • 被删除的记录的deleted_flag 从0变成1
  • 被删除记录的上一条记录的next_record指向被删除记录的下一条记录;
  • supremum中n_owned数量由原来的5变成4(当前分组中)

         需要注意的是,图中显示的是虚线,实际上这条数据并没有被删除,这就产生了垃圾链,原因是为了复用空间,如果将这条数据直接删除的话,会出现如下问题:记录是顺序存储的,删除之后会出现碎片空间,如果不处理,会造成资源浪费,如果处理,后续的记录需要向前移动,会产生性能损耗;如果只是修改了记录的状态,后续恢复时只需要,将上述步骤进行反向操作即可,此时避免了分配空间。

6. Page Directory(页目录)— — 记录在页中的展现

        作用是在页中直接定位记录,提高查找性能

分组规则:        

  • Infimum单独一组
  • Supremum组允许出现1~8条记录
  • 普通组4~8条记录
  • 只统计非删除记录
Page Directory

         上图的分组即为满足分组条件之后分组情况,此时分组1中只有Infimum,分组2中有5条记录,其中最大的是Supremum(依照主键排列,而Supremum是最大的),上图中的Slot即为槽位,也就是page Directory,Slot指向组中最大的那条记录的next_record。
根据分组的规则,当没有数据的时候默认只有两个分组,Infimum和Supremum;新插入数据的时候,会放到Supremum分组中,当Supremum分组的记录已经有8条(含Supremum记录),再插入一条时,会进行组分裂,开新的组,Supremum会分出4条记录给新的组,此时三个分组的情况是 1.4.4,然后再将新的记录插入。

        查询:先查询page Directory,使用二分法查找,查到对应的slot即可指定到对应的组,由于组中都是单向链表形式,所以从组中的第一个元素,依次遍历查找即可,因为组中的记录数不多,所以此时效率也不会损耗很多。

7.页满后的插入操作

页满后的插入操作

         插入的时候指定id乱序插入,不使用order by查询出来的结果依然是根据id排序的,存储的时候就是根据主键排序。

        如果页已经满了,此时插入新的记录时,会申请一个新的页,此时根据主键排序,进行记录迁移,上图中插入的记录比已有的页中的最大用户记录小,此时会将原id为10的记录迁移到新的页中,而将新插入的id为7的记录插入到原10那个位置。 

        所以开发过程中在设计表时需要有一个id,设置自增,这样就可以避免因为主键排序导致的记录迁移,从而规避不必要的性能消耗。  

        主键自增有一个auto_increment锁,在单体的时候可以保证id唯一,但是分布式部署的时候,多台mysql没法保证id唯一,此时可以使用雪花算法生成唯一id来标记数据唯一,不同库的id相同也没所谓。 

         在进行记录迁移的过程中,heap_no也不会发生变化,查找记录根据next_record来查找,不根据heap_no

**************此文章只是本人学习过程中的学习笔记,不做其他用途,如果有其他意见,欢迎一起讨论,谢谢,侵删*************************

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值