大话MySQL原理

开场

众所周知,MySQL作为常用的数据库,我们仅仅学习它的使用方法已经不能够满足现在市场的竞争力了,正所谓知其然,知其所以然。了解MySQL的工作原理不仅可以让我们在平时的工作中对MySQL的使用更加得心应手,而且在面试的时候胜人一筹。但是MySQL原理内容过于复杂,各种概念是在让人头大,而我写这篇文章的目的就是为了能够使用让人更能理解的话术讲述MySQL中比较核心的原理,而不是所有的原理都会去讲,部分知识在实际运用中比较少见,但是有些知识确实非常常见的。有选择地了解这些原理可以让你快速记住而且应对自如。

行格式(记录格式)

介绍原理之前先简单介绍一下MySQL的行格式,主要是这个知识是比较基础的,所以也是后面介绍原理进行铺垫。

InnoDB是一个将表中的数据存储到磁盘的存储引擎。他会将数据划分成若干的页,以页作为基本单位和磁盘进行交互,InnoDB每页的大小一般是16KB,也就说,一般情况下,一次至少从磁盘里读取16KB的数据,一次也是将16KB的数据存到磁盘中。

我们平时都是以一条条记录向MySQL中存储数据,这些记录在磁盘上的存放形式也是被称为行格式或者记录格式。行格式有四种,分别是COMPACT ,REDUNDANT,DYNAMIC,COMPRESSED。我们重点讲COMPACT,话不多说,直接上图

下面依次介绍一下

  • 边长字段长度列表:主要是针对那个长度不确定的字段,比如 varchar等,因为他们具体多长是不固定的,所以我们在真实存储的时候需要将这些数据到底多长也记录下来,这样服务器在存储的时候就知道每个字段的长度,并留够具体的长度。

  • null标志位,记录那些字段是可能为null值的

  • 头信息里面有一些数据,具体那些字段网上比价多,我估计很少有人全记得(反正我记不住),但是我只记其中四个后面介绍的,其他的暂时不记了

    名称 大小(位) 描述
    n_owned 4 一个页面中的记录会被分成若干个组,每个组中有一个记录是"大哥大",其余记录都是"小弟",大哥大的记录的n_owned值代表该组中所有的记录条数,小弟的记录为0
    record_type 3 表示当前记录的类型,0表示普通记录,1表示B+🌲非叶子结点的目录项记录,也就是索引,2表示infimum最小值,3表示最大值supermum
    next_record 16 表示下一条记录的位置
    heap_no 13 表示当前记录在页面堆中的相对位置

    目前为止这些概念大家只需要混个脸熟就行了,不用理解,后面会说到。

再往后就是记录真实数据。

其他的几种类型的行数据和这个大差不差,就不介绍了(因为介绍多了你就晕了),等你这篇文章看完了,在回过头自己去搜一下其他几种类型和产别,对比一下就清楚了

InnoDB的数据页结构

前面我们说InnoDB的基本单位是页,那每一页有16KB,他们存着那些东西呢?难道都是我们的数据?直接上图

那么我们的数据是怎么存到userRecord中的呢?当一条记录来了之后,MySQL会在freeSpace中取出一部分空间划分到userRecord,并存贮数据。

userRecord

好,前面介绍了头信息,里面的数据,接下来就具体展开说说,heap_no记录了你在页中的第几条记录,同时规定了有两个虚拟记录,infimum表示最小值,每一页都是从这里开始,supermum表示最大值,每一页最后一条记录是这个。

next_record就非常重要了,前面介绍了infimum和supermum,不管是这两个虚拟记录还是用户的真实记录之间的连接,都是靠他,因为他记录了每条记录的下一条记录的位置

page Directory(页目录)

记录在页中是按照主键值由小到大的顺序串联成一个单向列表,那如果想根据主键值查找页中的某条记录怎么办呢?当然最笨的办法是从小到大按照next_record一个一个寻找,那这样肯定是效率低下的。所以MySQLInnoDB引擎就划分了page Directory ,

  • 页目录将所有的记录(包括最大和最小的虚拟记录,但是不包括删除的)都分成了若干组
  • 每个组最后一条记录就是"带头大哥",将大哥的头信息里的n_owned属性表示该组中有几条记录。
  • 将每个组的最后一条记录在页面中的偏移量(也就是该记录的真实数据与页面中第0个字节的距离)单独提取出来,按顺序存放在页尾部的位置。这些偏移量称为槽(Slot),页目录就是由多个槽组成的。

page Header 和file Header 里面的东西不重要

索引

前面介绍了数据在页中怎么存储,也介绍了在单个页中怎么查找(通过槽),那么在多个页之间怎么快速查找呢?

先回顾一下页和记录之间的关系,上图

可以看到,每个页之间在物理上可能不是连续的,但是是通过双向链表关联的。

首先我们简化一下前面介绍的行格式

这样每个页中就是这样了

最上面表示了页的页号(也就是10),📢上下图的颜色表示的意思的是样的,紫色是record_type,蓝色是next_record,其他是列值

插入数据是首先页号有可能不是连续的,但是尽量会连续。同时也会将按照主键由小到大排序,如下图

这个时候我们按照之前页目录的思想,将每个页的页号和每个页的第一个记录(最小的)主键做一个映射关系。如下图

这时候我们就可以根据这个映射关系找到对应的页。那现在问题来了,怎么存放这个映射关系呢?答案就是使用页,和页里的记录。如下图

首先我们将这种映射关系称为索引,那我们怎么区分页是记录用户数据还是索引呢?那就靠每条记录的record_type,当值为1时,就是表示这是索引项。

其次就是在索引页里的记录上,每条数据和存放用户数据的记录是不一样的,那就是它只存放了每一页最小的主键值和对应的页号,并没有存放用户数据。

还有就是那么多的页,光靠一页索引肯定是不行的。那就必须要很多页并维护他们之间的关系,所以索引页之间就通过双向连接来管理。

那我么怎么根据主键值快速在索引里定位记录存在的页数呢?那就是生成一个更高级的目录,也就是套娃🪆思想啦。最后就是如上图所示的那样啦

这种结构就是常说的B+🌲。由此可见B+🌲的"索引即数据,数据即索引",这种

  • 使用主键值大小排序
  • B+🌲的叶子节点存储用户数据

称为聚簇索引

同时也有不满足第一点的索引,也就说以非主键列的大小为排序规则建立的索引,这种称为二级索引 。这种索引需要执行回表操作后才能查询到完整的用户记录

还有就是以多个列的大小为排序规则,也就是为多个列创建索引,称为联合索引。也称复合索引。复合索引的优势是一个索引可以查询多个条件,对比一个条件设置一个索引,更节省空间和时间

索引代价

创建索引不是越多越好,

  • 空间上,每建立一个索引都要为他建立一颗B+🌲,每个节点都是一页,一页是16KB,所以占用存储空间
  • 时间上,每当对表进行增删改操作,都会修改各个B+🌲的索引,而且页和页之间是双向列表,页内是有大小规则排序的单向列表,所以也会更新链表

应用B+🌲

比如我们遇到一个查询id>100的我们将条件形成的大于100的id称为扫描区间 ,把条件称为形成这个扫描区间的边界条件 ,如果这个区间质保函一个值,我们称为单点扫描区间 ,有范围的称为范围扫描区间

当我们的条件满足形成一个合理的扫描区间,也就说所有条件单独所形成的区间具有一定的合集,重叠。这样才是有效的扫描区间。

LIAKE关键字只有匹配完整字符串和字符串前缀才有效

联合索引和最左匹配原则

对于联合索引,如(a=1 and b > 2 and c>3),首先会按照a字段大小进行排序,当匹配到1时,因为是固定值,形成单点扫描区间,才会按照b进行排序,同理当b是单一值,形成单点扫描区间后再按照c进行排序

但是就上面例子而言,对于c来说,b并不是单点扫描区间,所以满足的b有很多,c有序的前提是b的值确定

最左原则就是在MySQL建立联合索引时会遵守最左前缀匹配原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。当遇到(<,>,LIKE不匹配字符串的情况下,between)等能形成范围扫描区间的字段就是中断

也就所上面例子只有a,b会使用到联合索引,所以我们只用建立a,b的联合索引就可以了。

什么情况下创建索引

  • 区分度高字段可以建立
  • 频繁作为查询条件、分组条件的字段
  • 查询中与其他表关联的字段,外键关系建立索引

什么情况下不需要建立

  • 表记录太少
  • 经常增删改的表或者字段(例如电商项目的用户余额)
  • Where条件里用不到的字段不创建索引
  • 过滤性不好的不适合建索引(手机号、身份证号之类过滤性好)

为了管理页,MySQL的InnoDB服务器设计了表空间文件空间)的概念,这是一个抽象概念(就是划分一下,方便管理,真实不存在)

MySQL的数据目录是/usr/local/var/mysql,可以通过语句SHOW BARIABLES LIKE 'datadir'查询,数据目录下还包括

  • 服务器进程文件,进程ID
  • 服务器日志文件
  • ssl、rsa、证书和密钥
相关推荐
拉不动的猪26 分钟前
electron的主进程与渲染进程之间的通信
前端·javascript·面试
uhakadotcom30 分钟前
零基础玩转千卡训练!Modalities框架中文指南:从安装到实战的全解析
算法·面试·github
uhakadotcom1 小时前
云原生数据仓库对比:Snowflake、Databricks与阿里云MaxCompute
后端·面试·github
多多*2 小时前
JavaEE企业级开发 延迟双删+版本号机制(乐观锁) 事务保证redis和mysql的数据一致性 示例
java·运维·数据库·redis·mysql·java-ee·wpf
uhakadotcom2 小时前
PostgreSQL 行级安全性(RLS)简介
后端·面试·github
转转技术团队3 小时前
"慢SQL"治理的几点思考
数据库·mysql·性能优化
bingbingyihao3 小时前
Windows下安装常用软件--MySQL篇
windows·mysql
m0_653031363 小时前
加新题了,MySQL 8.0 OCP 认证考试 题库更新
数据库·mysql·开闭原则
uhakadotcom3 小时前
Tableau入门:数据可视化的强大工具
后端·面试·github
hrrrrb4 小时前
【MySQL】锁机制
数据库·mysql