MySQL的存储引擎与架构

110 阅读5分钟

MySQL架构

MySQL采用客户端/服务器架构,客户端与MySQL服务器建立TCP连接并通过SQL语句与MySQL服务器交互。MySQL服务器可分为三层架构:

  • 连接层:负责建立TCP连接,验证用户名、密码、权限,从线程池分配线程给客户端
  • 服务层:对SQL语句做预处理
    • 缓存:对SQL语句的结果进行缓存,当SQL语句相同时直接返回结果,但是命中率较低,且数据更新后相关缓存都会失效,故在8.0后已废弃
    • 解析器:对SQL语句进行语法语义分析,确保SQL语法正确
    • 优化器:确定SQL语句的执行计划,包括选择索引、表连接顺序、子查询优化、加锁策略等,这些也是SQL语句优化的主要方向
  • 引擎层:负责MySQL中数据的存储和读取,主要存储引擎包括InnoDB,MyISAM,Memory等

MySQL存储引擎

  • InnoDB:MySQL默认存储引擎。支持事务、外键、表级锁和行级锁、主键索引使用的是聚簇索引。InnoDB提供高并发并保证数据完整性,适合高并发场景或复杂事务场景。
  • MyISAM:非事务处理存储引擎。不支持事务,行级锁,外键,索引使用的是非聚簇索引,保存表的具体行数。MyISAM是早期使用的存储引擎,适合只读或低并发写入的应用场景。
  • Memory:将所有数据存储在内存中,访问速度快,但重启会丢失数据,适合做缓存。

SQL语句执行顺序/流程

以查询语句为例,SQL语句执行顺序为:

  • from选择表
  • join on连接表
  • where条件筛选
  • group by分组聚合
  • having分组后的条件过滤
  • select选择查询的列
  • order by对查询结果进行排序
  • limit限制返回多少条查询结果

执行顺序决定了where无法使用聚合函数,无法使用select定义的别名。

SQL语句在底层的执行流程为:

  • 客户端通过TCP连接发送请求到MySQL服务器,服务器进行权限验证,分配线程
  • 先查看缓存,当两条SQL语句完全一样时返回缓存结果
  • 再由解析器对SQL语句进行语义分析,若SQL语句正确则生成语法树
  • 再由优化器确定SQL语句的执行计划,决定是否使用索引,表之间如何连接等
  • 最后由执行器调用存储引擎的API完成具体操作,将结果集返回给客户端

如果是更新语句的话,还要检查是否有排他锁,写undolog,redolog,binlog,刷盘,commit事务

MySQL数据存储形式

MySQL的底层数据结构为B+树(后续文章中介绍),而B+树存储的数据是整个表,表空间由段、区、页、行组成,如下图所示

image.png

  • 段:表空间由多个段组成,数据段用来存储B+树叶节点的区,索引段用来存储B+树非叶节点的区,回滚段存储回滚数据的区
  • 区:区处于段和页中间,段由多个区组成,区是一组连续的页(64页,1M数据)。B+树中的相邻节点都位于一个区,在磁盘中连续,可以使用顺序IO而不是随机IO,加快了范围查询的速度
  • 页:页是InnoDB读写数据的基本单位(16KB),页的空间是连续的,B+树的一个节点就是一页,数据库每次读写最少16KB数据(提高读写效率)
  • 行:页中即为一行一行的数据,默认行格式为Dynamic。行格式具体分为:额外信息,隐藏字段,真实数据,如下图所示。

image.png

  • 记录的额外信息
    • 变长字段长度列表:存储如varchar类型的数据长度。(非必须)
    • NULL值列表:真实数据中不会存NULL值,NULL值统一存在NULL值列表中,每个列对应一个二进制位,对应位为1时表示对应列的值为NULL。(非必须)
    • 记录头信息:包括删除字段(标记该条数据是否被删除),下一条记录的位置等
  • 隐藏字段
    • row_id:若建表时指定了主键或唯一约束,则没有该隐藏字段,否则添加row_id,因为当没有指定主键和唯一约束时InnoDB会使用row_id作为聚簇索引
    • trx_id:表示该数据是由哪个事务生成的,用于实现MVCC
    • roll_pointer:记录该数据上一个版本(undolog中)的指针,用于实现MVCC

其他问题:

  • varchar(n)中的n最大取值为多少
    • 首先一行占用的字节数不能超过65535,存储实际的字符数要看使用的字符集,还要受行格式中的额外信息的影响
  • 行溢出后MySQL怎么处理
    • MySQL中磁盘与内存交互的单位是页,当一条记录大到一页存不下时就会发生行溢出,多出的数据会存到溢出页中(当前页保存溢出页地址)
  • IP地址如何存储
    • 可以使用varchar(15)存储,因为IP地址最少7个字符(0.0.0.1),最多15个字符(225.225.225.225),但是更高效的方式是使用无符号整数存储,因为IP地址是32位无符号整数,直接使用unsigned int存储只需要4字节,并且MySQL提供了转义函数INET_ATON,这样可以节省存储空间,便于使用范围查询。
  • 图片如何存储
    • 一般不直接存储图片文件,而是使用varchar存储图片文件的路径
  • emoji表情如何存储
    • 每个表情对应一个unicode编码,存储编码即可,但是要使用uft8-mb4编码
  • 长文章如何存储
    • 使用LongTEXT类型存储,最多可以存储4GB数据,文章太大时也可以将其存储到外部文件系统中,MySQL保存文件路径
  • 金额如何存储
    • 使用decimal类型,避免浮点数的精度损失问题(float底层使用二进制表示)