当前位置: 首页 > ai >正文

十、MySQL InnoDB引擎底层解析

一、InnoDB记录的存储结构和索引页结构

1、InnoDB的三大特性

双写机制、Buffer Pool、自适应Hash索引。

自适应Hash索引:

  • 针对热点数据创建hash索引,下一次查询的sql命中这些数据时,可以直接返回
  • 是innoDB底层自动维护的索引,无法手动干预

2、引入需求

客户端、表存储在哪里?

表中数据是什么格式?

以什么方式去访问这些数据?

事务和锁的原理?

3、如何从磁盘中读取数据

数据分成页(磁盘 -> 内存),每页16KB;

4、行格式

对于开发人员来说,以条数为单位,磁盘中存放一条数据叫行格式,有以下几类

  1. COMPACT;
  2. Redundant;
  3. Dynamic; // 默认
  4. Compressed;

查询行格式

show variables like 'innodb_default_row_format';

5、行格式结构 & 说明

以COMPACT展示:

部分

说明

变长字段长度列表

所有【变长的字段】的长度组成的列表;

varchar数据类型;

最大的数据长度 <= 255字节,就用1个字节记录,大于就用2个字节记录

NULL值列表

存储当前行【所有字段】是否为null,用0和1表示;

  • 1 - null
  • 0 - 非null

记录头信息

5个字节构成,按二进制的位数存储,40位;

_____________________________________________________________________

|  头信息  |   二进制位数  |  解释                                                   |

  预留位1          1                没有使用;

  预留位2          1                没有使用;

  delete_mask   1                标记是否被删除;

  min_rec_mask 1                B+树的每层非叶子节点中的最小记录都会添加该标记;

  n_owned        4                表示当前记录拥有的记录数;

  heap_no        13               表示当前记录的类型:0-普通记录,1-B+树非叶子节点记录,2-表示最小记录,3- 表示最大记录;

  next_record   16               表示下一条记录的相对位置;

隐藏信息--DB_ROW_ID

如果没设置表的主键列,那就自动创建这一列做主键;

隐藏信息--DB_TRX_ID

事务的id

隐藏信息--DB_ROLL_PTR

回滚指针

6、数据溢出

定义:一个字段的大小(varchar 60000字节)就大于一页(16KB = 16384字节)

处理方法:

行格式

处理方法

COMPACT;

Redundant;

化整为零法;

当前页只保存【所有字段】的前768个字节的数据;

剩余的数据存到其他页中,留出20个字节指向其他页;

Dynamic;

Compressed;

别处寄存法;

如果数据长度超过768个字节,那么当前页什么都不保存;

只利用20字节指向其他页; 

7、索引页格式

聚簇索引:关联了数据,可以理解成数据页。

二、表空间

表空间是抽象概念,是数据目录下的一个文件(ibdata1)。

系统表空间

  • MySQL只有一个系统表空间,用来保存额外的数据,和系统数据
  • InnoDB数据字典: 以B+树保存表信息;

独立表空间:

  • 行在页中,有若干条; //可能一个页放多条数据,可能多个页才放得下一条数据;
  • 页在区中,有64页,每个页是物理连续的,消除大量随机 I/O 
  • 区在组中,有256个区;
  • 组在段中,有若干个组,是个逻辑的分组;// 应对范围查询
  • 段在表空间中,有页节点段,非页节点段,回滚段; 

三、InnoDB的双写缓冲区/双写机制(doublewrite buffer)

说明

目的

innodb按页(16KB)做单位,操作系统按4KB做单位;

防止一个页写入了4KB后断电,出现页缺失情况;

作用

保证数据写入的可靠性,用来防止丢失数据;

组成

双写缓冲区在系统表空间中;

包含两个区,分别是:extent1区(1MB)和extent2区(1MB)

这两个区的物理地址是连续的,写入的快

共128个页,页的编号 64 ~ 192,第一个区和第二个区;

机制

把数据页写入数据文件前,先向双写缓冲区的两个区写入数据,两个区都写入完成后,才会再将数据页写入数据文件的合适位置;

写2次磁盘,第一次写双写缓冲区(磁盘),第二次真正写入数据文件(磁盘);

如果在写页的时候出现意外,可以在双写缓冲区找到备份完成恢复;

理解

先把要写入的数据快速的保存到一个磁盘,再慢慢分发到正确的位置;

相当于一个事务,一个页中的数据要么都插入,要么都失败;

四、InnoDB的Buffer Pool

1、定义

MySQL服务启动时,申请一块连续内存用来快速操作数据,这块连续内存叫缓冲池(buffer pool);

访问MySQL数据时,如果在磁盘直接访问,会很慢。所以把数据以页为单位磁盘加载到内存中;

访问磁盘中的数据页A时,A从磁盘加载到buffer Pool,再做操作;

如果已经加载了,直接操作;

2、结构

缓存页存放的是某个操作命中的MySQL在磁盘中的数据

每个缓存页都有一个它自己的控制块;

控制块:占用5%左右的空间,存放控制相关信息;指示当前数据页的区号、页号、在缓冲池中的位置等信息;

数据页的编号 = A的表空间号 + A的页号;

3、free链表

空闲链表,一个双向列表。

作用

管理缓存页的分配;

控制buffer pool的时候可以从这个列表入手;

只要拿到一个控制块,那么它指向的缓冲页就拿到了;

结构

由许多控制块组成,可以理解成一个List;

​List包括:start,end,count = n,控制块;​

start -> 控制块1 <-> 控制块2 <-> end;​

初始化

在初始化缓冲池的时候创建free List,里面的每个节点都是空的;

使用过程

在数据页进入buffer pool的时候,找到其中的一个控制块,拿到这个控制块指向的缓存页;

  • ​向控制块写入控制信息;
  • 向缓存页存储读取的数据;​

List中的控制块被写入了数据,就认为该缓存空间被使用,把控制块移除出空闲链表;

4、脏页

背景: 数据在做修改时,要先修改内存buffer pool,后写磁盘;

问题: 如果只来得及修改内存,还没有写磁盘,这时称buffer pool中的页为“脏页”

5、flush链表(刷新链表)

作用

针对数据修改,存储脏页,批量地把脏页写入磁盘;

背景

数据在做修改时,会出现脏页;

机制

内存中buffer pool的数据页被修改后,不立即写入到磁盘,而是攒一波后批量提交;

​6、LRU链表(最少使用淘汰链表)

结构​

与free链表结构相似;

free链表移除出来的控制块,放到LRU链表的第一格;

作用

针对缓存淘汰控制,最少使用原则;

只保留最近的数据页,移除最早的数据页;

机制

​-- 淘汰步骤:

1、buffer pool中没有数据页时,把数据页A保存到LRU的第一个控制块上;

2、数据页B进来时,把B放到第一个控制块上,把A往后挪一格;

3、数据页C进来时,AB都往后挪一格;

4、再次命中A时,A放到第一格,BC都往后挪一格;

5、如果空间不够,那就淘汰最后一格的数据页;

7、LRU淘汰算法遇到的问题

InnoDB中的预读:

执行SQL时,查询优化器会预先加载可能用到的数据页到buffer pool;

  • 随机预读:在读取某个区中的数据页时,如果连续读取了13个以上的页,使用异步线程预读取当前区的所有页;
  • -- 获取随机预读开关状态,默认关闭
    show variables like 'innodb_random_read_ahead';
  • 线性读取:在读取某个区中的数据页时,如果连续读取了56个以上的页,MySQL会自动启动一个异步线程,提前读取下一个区中的所有页;
  • -- 获取线性预读阈值
    show variables like 'innodb_read_ahead_threshold'; 

在预读数据后,会大量的将已有缓存页顶没了;

SQL语句触发全表扫描:

进行表关联,一张表可能扫描多次;

每次扫描都会淘汰一次LRU链表;

8、LRU的链表的优化

InnoDB把LRU链表分成了两段:

  • young区域(热数据 63%)
  • old区域(冷数据 37%)
-- 查询划分规则:
show variables like 'innodb_old_blocks_pct'; 

对预读的优化

InnoDB规定数据页初次从磁盘加载(只是加载,没有读取)到buffer pool中,该缓冲页对应的控制块会放到old区域的头部;这样预读页就只会在old区域,不会进入young区域;

对全表扫描的优化

虽然首次加载的数据页放到old区域的头部,但是全表扫描涉及到了数据页的读取,第一次访问的时候就会将数据页放到young区域;

所以InnoDB规定:对old区域的缓冲页进行第一次访问时,在它的控制块中写入访问的时间;如果后续的访问时间与第一次的访问时间超出了1s(1000ms),才将数据页放到young区域;

总结

InnoDB规定了两件事:

  • 新来的数据页要放在old区域
  • 第一次读取old区域的页要记录时间;

五、Buffer Pool的flush链表的管理

待补充

六、Buffer Pool的LRU链表的管理

待补充

http://www.xdnf.cn/news/13001.html

相关文章:

  • LLMs 系列实操科普(2)
  • FTP下载Argo数据
  • 【Docker 03】Docker Registry - 镜像仓库
  • Unity | AmplifyShaderEditor插件基础(第六集:平面波动shader)
  • 多模态大模型:AI的下一个前沿
  • ES Serverless 8.17王牌发布:向量检索「火力全开」,智能扩缩「秒级响应」!
  • 六.原型模式
  • docker nginx解决跨域请求的处理(https的也支持)
  • tomcat入门
  • 盟接之桥EDI软件:为制造业打造高效、安全的数据桥梁
  • 数据结构之队列
  • 基于SpringBoot实现的汽车资讯网站设计与实现【源码+文档】
  • CppCon 2015 学习:Simple, Extensible Pattern Matching in C++14
  • AI重塑SEO关键词精准策略
  • Linux离线(zip方式)安装docker
  • 能源即服务:智慧移动充电桩的供给模式创新
  • 网络安全:数字时代的守护盾
  • 爬虫基础学习day2
  • 解密鸿蒙系统的隐私护城河:从权限动态管控到生物数据加密的全链路防护
  • C++编译之导入库理解与使用
  • React Hooks 的原理、常用函数及用途详解
  • crackme006
  • 抽象类和接口(全)
  • 98.错误走百度翻译API的苦98步
  • 深入浅出JavaScript中的ArrayBuffer:二进制数据的“瑞士军刀”
  • 从数据到价值:企业构建大数据价值链的核心战略
  • 闭合逻辑检测(保留最大连通分量)
  • 浏览器中 SignalR 连接示例及注意事项
  • 信创领域下的等保合规建设及解读
  • ava多线程实现HTTP断点续传:原理、设计与代码实现