浅谈Mysql的sql执行过程

102 阅读5分钟

image.png

一、介绍

一条sql在mysql底层是如何执行,底层原理到底是怎么样的呢,下面我们先来介绍一些组成部分。

二、组成

1、MySQL 驱动

  • 帮应用系统跟mysql数据库建立连接

  • 一次 SQL 请求就会建立一个连接

  • 请求协议

    • TCP/IP

2、数据库连接池

建立的连接并不是每次请求都会去创建的,而是从数据库连接池中去获取,这样就解决了因为反复的创建和销毁连接而带来的性能损耗问题了。

3、查询解析器

SQL 是写给我们人看的,机器哪里知道你在说什么?这个时候解析器就上场了。他会将 SQL 接口传递过来的 SQL 语句进行解析,翻译成 MySQL 自己能认识的语言

4、查询优化器

MySQL 会帮我们去使用他自己认为的最好的方式去优化这条 SQL 语句,并生成一条条的执行计划,比如你创建了多个索引,MySQL 会依据成本最小原则来选择使用对应的索引,这里的成本主要包括两个方面, IO 成本和 CPU 成本

5、执行器

执行器最终会根据一系列的执行计划去调用存储引擎的接口去完成 SQL 的执行

6、存储引擎

真正执行 SQL 的动作是在存储引擎中完成的,通过存储引擎去缓存或者磁盘获取数据

7、Buffer Pool

  • 定义

    • buffer pool 是一块内存区域,为了「提高数据库的性能」,当数据库操作数据的时候,把硬盘上的数据加载到 buffer pool,不直接和硬盘打交道,操作的是 buffer pool 里面的数据,数据库的增删改查都是在 buffer pool 上进行,buffer pool 里面缓存的数据内容也是一个个数据页
  • 链表

    • free 链表

      • 用于帮助我们找到空闲的缓存页
    • flush 链表

      • 用于找到脏缓存页,也就是需要刷盘的缓存页
    • lru 链表

      • 用来淘汰不常被访问的缓存页,分为热数据区和冷数据区,冷数据区主要存放那些不常被用到的数据
  • 预读机制

    • 存储引擎的接口在被 Server 层调用时,会在响应的同时进行预判,将下次可能用到的数据和索引加载到 Buffer Pool

8、undo log

  • 数据加载到 Buffer Pool 中同时会往 undo 日志文件中插入一条日志,记录数据修改前的样子
  • 目的是支持事务
  • 更新操作是在Buffer Pool中执行的

9、redo log

  • redo日志保持了事务的持久性,确保即使在系统崩溃的情况下,只要 redo 日志还在,就可以重做事务,从而保障数据不会丢失。
  • 当事务修改的脏页被刷新到磁盘上时,redo日志的主要任务就完成了。之后,这部分占用的重做日志空间可以被重用(覆盖)。
  • redo 日志文件是 InnoDB 特有的,他是存储引擎级别的,不是 MySQL 级别的

10、binlog

  • 定义

    • 记录整个操作过程
  • 刷盘策略

    • sync_bin log参数

      • 默认为 0,表示先写入 os cache,也就是说在提交事务的时候,数据不会直接到磁盘中,这样如果宕机bin log数据仍然会丢失
      • 1 表示直接将数据写入到磁盘文件中,建议
  • 刷入模式

    • STATMENT

      • 定义

        • 基于 SQL 语句的复制,每一条会修改数据的 SQL 语句会记录到 bin log 中
      • 优点

        • 不需要记录每一行的变化,减少了 bin log 日志量,节约了 IO , 从而提高了性能
      • 缺点

        • 在某些情况下会导致主从数据不一致,比如执行sysdate()、slepp()等,实时时间不一样
    • ROW

      • 定义

        • 基于行的复制,仅需记录哪条数据被修改了
      • 优点

        • 不会出现某些特定情况下的存储过程、或 function、或 trigger 的调用和触发无法被正确复制的问题
      • 缺点

        • 会产生大量的日志,尤其是 alter table 的时候会让日志暴涨
    • MIXED

      • 定义

        • 混合模式,一般的复制使用 STATEMENT 模式保存 bin log ,对于 STATEMENT 模式无法复制的操作使用 ROW 模式保存 bin log

三、流程

  • 准备更新一条 SQL 语句

  • 通过mysql驱动建立连接,连接从数据库连接池获取

  • 查询解析器解析sql

  • 查询优化器优化sql生成执行计划

  • 执行器根据一系列的执行计划去调用存储引擎的接口去完成 SQL 的执行

  • MySQL(innodb)首先会先去缓冲池(BufferPool)中去查找这条数据,没找到就会去磁盘中查找,如果查找到就会将这条数据加载到缓冲池(BufferPool)中

  • 在加载到 Buffer Pool 的同时,会将这条数据的原始记录保存到 undo 日志文件中

  • innodb 会在 Buffer Pool 中执行更新操作

  • 更新后的数据会记录在 redo log buffer 中

    • MySQL 为了提高效率,所以将这些操作都先放在内存中去完成,然后会在某个时机将其持久化到磁盘中
  • 完成以后就可以提交事务,在提交的同时会做以下三件事

  • (第一件事)将redo log buffer中的数据刷入到 redo log 文件中

  • (第二件事)将本次操作记录写入到 binlog文件中

  • (第三件事)将 binlog 文件名字和更新内容在 binlog 中的位置记录到redo log中,同时在 redo log 最后添加 commit 标记

    • 如果在数据被写入到binlog文件的时候,刚写完,数据库宕机了,数据会丢失吗? 首先可以确定的是,只要redo log最后没有 commit 标记,说明本次的事务一定是失败的。但是数据是没有丢失了,因为已经被记录到redo log的磁盘文件中了。在 MySQL 重启的时候,就会将 redo log 中的数据恢复(加载)到Buffer Pool中。
  • 至此表示整个更新事务已经完成

  • MySQL 会有一个后台线程,它会在某个时机将我们Buffer Pool中的脏数据刷到 MySQL 数据库中,这样就将内存和数据库的数据保持统一了