zoukankan      html  css  js  c++  java
  • 【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
  • 相关阅读:
    PAT 甲级 1126 Eulerian Path (25 分)
    PAT 甲级 1126 Eulerian Path (25 分)
    PAT 甲级 1125 Chain the Ropes (25 分)
    PAT 甲级 1125 Chain the Ropes (25 分)
    PAT 甲级 1124 Raffle for Weibo Followers (20 分)
    PAT 甲级 1124 Raffle for Weibo Followers (20 分)
    PAT 甲级 1131 Subway Map (30 分)
    PAT 甲级 1131 Subway Map (30 分)
    AcWing 906. 区间分组 区间贪心
    AcWing 907. 区间覆盖 区间贪心
  • 原文地址:https://www.cnblogs.com/healkerzk/p/14103961.html
Copyright © 2011-2022 走看看