• 【mysql】sql执行流程


    mysql官方文档 https://dev.mysql.com/doc/refman/

    一条查询语句的执行流程

    1. 连接

    Mysql服务监听的端口默认为3306,有专门负责处理连接的模块,连接是需要权限验证。

    如何查看mysql的连接数?

      show global status 'Thread%';
    

    字段 含义
    Threads_cached 缓存中的线程连接数
    Threads_connected 当前打开的连接数
    Threads_created 为处理连接创建的线程数
    Threads_running 非睡眠状态的连接数,通常指并发连接数

    为何查看mysl的连接数是“show Thread”查看线程数呢?

    因为客户端每产生一个连接或一个会话,在服务器端就会创建一个线程来处理。反过来,如果要结束会话,就需要杀死进程。

    每一个连接都分配线程的话,毋庸置疑是需要消耗服务端资源的,所以在连接时长连接数(并发量)上mysql就做了些处理。

    1. 连接时长:mysql会把长时间不活动(sleep)的连接自动断开。
       show variables like 'wait_timeout';  --非交互式超时时间,如JDBC程序
    

       show variables like 'max_connections';   --交互式超时时间,如数据库工具
    

    交互式和非交互式的默认连接超时时长都是28800秒(8小时)。

    1. 连接数:mysql服务允许最大的连接数(并发数)是多少?
      show variables like 'max_connections';
    

    下图中的最大连接数为200(这里是我自己做了修改),在mysql5.7和目前的mysql8.0的版本中,mysql的默认最大连接数为151,最大可支持设置成100000(10w)

    mysql8.0官网关于max_connections的描述

    2. 查询缓存

    mysql中查询缓存默认为关闭状态(不推荐使用),且mysql8.0中已经将查询缓存移除了。需要缓存还是交给ORM(如:mybatis默认开启一级缓存)框架或redis等第三方服务来实现。

     show variables like 'query_cache%';
    

    3. 语法解析和预处理

    主要是对sql语句基于SQL语法进行词法分析语法分析以及语义解析

    3.1 词法分析:就是把一条sql语句分成一个个单词。

    select * from student where student = '1';
    

    会分成select、*、from、student、where、student、=、'1'八个单词,每个单词从哪开始从哪结束,是什么类型。

    3.2 语法分析
    及对SQL做一些语法检查,比如单引号是否闭合、识别关键字等,然后根据SQL语法规则,生成解析树(select_lex)。

    3.3 预处理器
    在语法分析的基础上(解决语法分析无法解析的语义),对表名、列名是否存在、别名是否异常等问题进行解析处理,进一步生成一个新的解析树。

    4.查询优化和查询执行计划

    4.1 查询优化器
    查询优化器的目的就是根据解析树生成不同的执行计划(Execution Plan),然后选择一种最优的执行计划,MySQL里面使用的是基于开销(cost)的优化器,哪种计划开销最小,就用哪种。
    查看查询的开销:

    show status like 'Last_query_cost';
    

    4.2 优化器都做哪些优化?
    如:
    两表关联查询时,以哪个表为基准表;
    多个索引可以使用时,使用哪个索引等等。

    以下来自《数据库查询优化器艺术-原理解析与SQL性能优化》
    4.2.1 子查询优化
    4.2.2 等价谓词重写
    4.2.3 条件简化
    4.2.4 外连接消除
    4.2.5 嵌套连接消除
    4.2.6 连接的消除
    4.2.7 语义优化
    4.2.8 非SPJ优化

    优化完之后,优化器会把解析树变成一个查询执行计划,查询执行计划是一个数据结构。
    可以通过在sql语句前加上explain来查看执行计划的信息
    如:

    EXPLAIN select name from student where id = 1;
    

    获取详细信息:

    EXPLAIN FORMAT=JSON select name from student where id = 1;
    

    5.存储引擎

    mysql支持多种存储引擎,常用的有MyISAM和InnoDB,5.5.5之前mysql默认的存储引擎为MyISAM,5.5.5之后mysql默认的存储引擎为InnoDB。

    常见的存储引擎
    5.7 https://dev.mysql.com/doc/refman/5.7/en/storage-engines.html
    8.0 https://dev.mysql.com/doc/refman/8.0/en/storage-engines.html

    MyISAM

    • 通常用于只读或以读为主的工作,表级锁定限制了读写性能。
      特点:
    • 支持表级别的锁(插入和更新会锁表)。不支持事物。
    • 拥有较高的插入(insert)和查询(select)速度。
    • 存储了表的行数(count速度更快)。

    tips: 怎么快速向数据库插入100w条数据?

    • 可以先用MyISAM插入数据,然后修改存储引擎为InnoDB。

    InnoDB

    5.7、8.0版本中默认的存储引擎,适合经常更新的表,存在并发读写或者有事务处理的业务系统

    • 支持事务,支持外键,因此数据的完整性,一致性更高
    • 支持行级别的锁和表级别的锁
    • 支持读写并发,写不阻塞读(MVCC)。啥是MVCC?以后再说。
    • 特殊的索引存放方式,可以减少IO,提升查询效率

    一条更新语句是如何执行的

    执行流程简述

    一个简化后的过程(因为更新操作涉及到事务,这里先记一个大概的流程示例)

    要将student表中id=1的学生姓名(原为lisi)修改为zhangsan,执行sql语句

    update student set name='zhangsan' where id=1;

    1. 事务开始,从内存(buffer pool)或磁盘取到包含这条数据的数据页,返回给Server的执行器;
    2. 执行器修改数据页的一行数据;
    3. 记录修改之前的内容到undo log,如update student set name='lisi' where id=1;;
    4. 记录要修改的操作到redo log,如update student set name='zhangsan' where id=1;
    5. 调用存储引擎接口,记录数据页到buffer pool
    6. 事务提交。

    缓冲池 Buffer Pool

    InnoDB设定了一个存储引擎从磁盘读取数据到内存的最小单位,叫做

    操作系统也有页的概念。操作系统的页大小一般是4k(传闻中的4k对齐),在InnoDB中,这个最小的单位默认是16KB大小。若需要修改这个值的话,修改后需要清空数据重新初始化服务。

    https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html#sysvar_innodb_page_size

    也就是说InnoDB存储引擎从磁盘读取数据的时候,每次最少读16KB的数据,我们所需要的操作的数据就在这样的页里面,也就是常常说的数据页。

    而我们每次拿数据如果都从磁盘中取出来放入内存的话,还是避免不了频繁io消耗资源的问题,这里就还是需要一个缓存的思想,把读取过的数据页缓存起来。

    InnoDB设计了一个内存的缓冲区。读取数据的是会,先判断缓冲区内是否存在,若存在则直接取用,不存在则从磁盘读取后将数据放入这个内存的缓冲区内。这个缓冲区就叫做Buffer Pool

    修改数据时,也是先写到buffer pool,而不是直接写到磁盘。内存的数据页和磁盘数据不一致的时候,我们把内存取的这部分数据叫做脏页。InnoDB中有专门的后台线程把buffer pool的数据写到磁盘,每隔一段时间就会一次性的把多个修改写入(同步)的磁盘,这个动作就叫做刷脏

    有次可见,Buffer Pool的作用就是为了提高读写的效率。

    redo log

    因为刷脏不是实时的,如果Buffer Pool里面的脏页没有同步到磁盘时,服务器或者数据库宕机或者重启,这些数据就会丢失。如何避免这部分数据的丢失,实现内存内数据的持久化呢?

    InnoDB把所有对“页”的修改操作写入到一个操作日志文件中。如果脏页中的内容没有同步到磁盘时,数据库再启动的时候,会从这个日志文件进行恢复操作(实现crash-safe)。我们说的事务的ACID中的D(持久性),就是用它来实现的。

    这个日志文件就叫做redo log(重做日志)。

    • 既然都要写磁盘,为何不直接写到DBFile里面,还要先写日志再写磁盘呢?

      • 这个与顺序io和随机io有关
      • 如果需要的数据是随机分散在磁盘的不同页的不同扇区中的,那么找到相应的数据需要等磁臂旋转到指定的页,然后盘片寻找到对应的扇区(寻址的过程),才能找到所需要的的一块数据,依次进行此过程(不断地重新寻址)直到找完所有数据,这个就是随机IO。
      • 顺序IO是指读写操作的访问地址连续。如盘片已经找到了第一块数据所在的扇区(寻址成功)后,并且其他所需的数据就在这一块数据后边,那么就不需要重新寻址,可以依次拿到所需的数据,这个就叫顺序IO。
      • 直接写数据文件(写数据(写聚簇索引)、写索引(普通索引))是随机I/O,而记录日志是顺序I/O(不断地追加),因此先把修改写入日志文件,在保证了内存数据安全性的情况下,可以延迟刷盘时机,进而提升系统吞吐量。

    • redo log特点

      • 为InnoDB提供了崩溃恢复的特性,实现持久性
      • redo log的大小是固定的,前面的内容会被覆盖,一旦写满,就会触发buffer pool到磁盘的同步,以便腾出空间记录后面的修改。
      • 默认有两个文件ib_logfile0和ib_logfile1,每个48m。

    可以通过以下命令查看InnoDB中redo log的相关参数:

    show variables like 'innodb_log%';
    

    参数 含义
    innodb_log_size 每个文件的大小,默认48M
    innodb_log_files_in_group 文件的数量,默认为2个
    innodb_log_group_home_dir 文件所在路径,如果不指定,则为datadir的路径

    除了redo log外,还有一个跟修改相关的日志,叫做undo log。redo log和undo log与实务密切相关,统称为事务日志。

    undo log

    undo log(撤销日志或回滚日志)记录了实务发生之前的数据状态,分为insert undo log和update updo log。如果修改数据时出现异常,可以用undo log来实现回滚操作(保持原子性)。

    show variables like '%undo%';
    

    参数 含义
    innodb_undo_directory uodo文件的路径
    innodb_undo_log_truncate 是否开启在线回收undo log日志文件
    innodb_max_undo_log_size undo文件的大小。如果开启了innodb_undo_log_truncate,超过这个大小的时候就会触发truncate回收动作,如果page大小是16kb,truncate后空间缩小到10M。默认1073741824字节=1G。
    innoidb_undo_tablespaces 设置undo独立表空间个数,范围为0-95,默认为0。0表示不开启独立undo表空间,且undo日志存储在ibdata文件中。
    innodb_undo_log_encrypt
  • 相关阅读:
    Visual Studio的输出窗口上输出调试信息的函数
    std::min error C2059: 语法错误:“::” 的解决方法
    error C2872: “flann”: 不明确的符号 --- PCL 与OpenCV2 的flann命名空间冲突问题的解决方法
    VS编译器中设置 输出窗口 只显示error,不显示warning 要如何配置
    nginx.conf的完整配置说明
    Nginx基本配置、性能优化指南
    Apache手册
    Apache 配置虚拟主机三种方式
    Linux常用命令汇总
    Linux下安装Apache
  • 原文地址:https://www.cnblogs.com/healkerzk/p/14103961.html
Copyright © 2020-2023  润新知