zoukankan      html  css  js  c++  java
  • 04 MYSQ的SQL优化需要了解的工具explain,profile,trace

    1 优化SQL前需要了解的工具,指令

    何时进行SQL语句的优化

    ​ 实际业务开发过程中,通常是先实现功能,然后再去优化。对于SQL语句也是如此,当然在开发过程中能够有意识的编写高效的SQL语句是一个需要不断学习的过程。

    1-1 数据分析:数据库各种操作的次数

    命令:查看各种操作次数

    show status like 'Com_______';        --7个短下划线 ,查看当前数据库当前session
    show global status like 'Com_______';  --查看当前整个数据库
    show global status like 'Innodb_rows_%';  --查看innodb引擎的数据表的情况
    

    • 上图中可以看到这次连接数据库的过程中当前数据库插入次数为4,事务提交次数为1等。

    1-2 定位低效率SQL语句

    两种方式:

    • 使用慢查询日志(在命令执行完之后分析日志,慢查询日志可以记录执行时间超过一定阀值的SQL语句,这个阀值由用户自己设定)
    • 利用show processlist(可以实时监控命令执行的效率)
      • 慢查询日志在查询结束以后才纪录,所以在应用反映执行效率出现问题的时候查询慢查询日志并不能定位问题,可以使用show processlist命令查看当前MySQL在进行的线程,包括线程的状态、是否锁表等,可以实时地查看 SQL 的执行情况,同时对一些锁表操作进行优化
    对于show processlist命令的测试

    show processlist每一列的含义(注意通过第6列可以查看哪些命令执行时间比较长):

    1) id列,用户登录mysql时,系统分配的"connection_id",可以使用函数connection_id()查看
    2) user列,显示当前用户。如果不是root,这个命令就只显示用户权限范围的sql语句
    3) host列,显示这个语句是从哪个ip的哪个端口上发的,可以用来跟踪出现问题语句的用户
    4) db列,显示这个进程目前连接的是哪个数据库
    5) command列,显示当前连接的执行的命令,一般取值为休眠(sleep),查询(query),连接(connect)等
    6) time列,显示这个状态持续的时间,单位是秒
    7) state列,显示使用当前连接的sql语句的状态,很重要的列。state描述的是语句执行中的某一个状态。
    个sql语句,以查询为例,可能需要经过copying to tmp table、sorting result、sending data等状态才可以完成
    8) info列,显示这个sql语句,是判断问题语句的一个重要依
    

    1-3 explain分析SQL执行语句(推荐直接看官方手册,参考资料04)

    explain查询出的信息说明(加粗的信息要重点关注)
    字段 含义
    id select查询的序列号,是一组数字,表示的是查询中执行select子句或者是操作表的顺序
    select_type 查询类型,主要用于区别 普通查询、联合查询(union、union all)、子查询等复杂查询。
    table 查询的记录所属的数据表
    type 可以理解为”数据的获取过程“,性能由好到差的为( system ---> const -----> eq_ref ------> ref -------> ref_or_null----> index_merge ---> index_subquery -----> range -----> index ------> all )
    possible_keys 显示可能应用在这张表的索引, 一个或多个
    key 表示实际使用的索引, 如果为NULL, 则没有使用索引
    key_len 表示索引中使用的字节数, 该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下, 长度越短越好 。
    rows 扫描行的数量
    extra 情况补充说明

    • 上面是查询的系统变量返回的explain记录。(@@是系统变量,@是会话变量)

    • 从上面的图片中可以看出图中查询语句
      • 在查询时使用的key(索引)是PRIMARY(主键索引)
    1-3-1 explain的id信息

    规律总结:id相同为一组,执行顺序按照上下排列,id不同时,id值越大,操作表的优先级越高

    • id 相同表示加载表的顺序是从上到下。
    • id 不同id值越大,优先级越高,越先被执行
    • id 有相同,也有不同,同时存在。id相同的可以认为是一组,从上往下顺序执行;在所有的组中,id的值越
      大,优先级越高,越先执行

    准备表

    注意点:唯一性约束通常是为了避免重复,字段可以为null.

    --01 创建角色表,id是主键,role_name添加唯一性约束,并且创建唯一索引unique_role_name
    CREATE TABLE `t_role` (
        `id` varchar(32) NOT NULL,
        `role_name` varchar(255) DEFAULT NULL,
        `role_code` varchar(255) DEFAULT NULL,
        `description` varchar(255) DEFAULT NULL,
        PRIMARY KEY (`id`),
        UNIQUE KEY `unique_role_name` (`role_name`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    --02 创建用户表,id是主键,username添加唯一性约束,并且创建唯一索引unique_role_name
    CREATE TABLE `t_user` (
        `id` varchar(32) NOT NULL,
        `username` varchar(45) NOT NULL,
        `password` varchar(96) NOT NULL,
        `name` varchar(45) NOT NULL,
     	PRIMARY KEY (`id`),
    	UNIQUE KEY `unique_user_username` (`username`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    --03 创建将用户与角色关联的表,该表与 用户表|角色表 存在主外关系
    CREATE TABLE `user_role` (
        `id` int(11) NOT NULL auto_increment ,
        `user_id` varchar(32) DEFAULT NULL,
        `role_id` varchar(32) DEFAULT NULL,
        PRIMARY KEY (`id`),
        KEY `fk_ur_user_id` (`user_id`),
        KEY `fk_ur_role_id` (`role_id`),
    	CONSTRAINT `fk_ur_role_id` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`) ON
    	DELETE NO ACTION ON UPDATE NO ACTION,
    	CONSTRAINT `fk_ur_user_id` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`) ON
    	DELETE NO ACTION ON UPDATE NO ACTION
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    --为3个表插入记录
    insert into `t_user` (`id`, `username`, `password`, `name`)
    values('1','super','$2a$10$TJ4TmCdK.X4wv/tCqHW14.w70U3CC33CeVncD3SLmyMXMknstqKRe','
    超级管理员');
    insert into `t_user` (`id`, `username`, `password`, `name`)
    values('2','admin','$2a$10$TJ4TmCdK.X4wv/tCqHW14.w70U3CC33CeVncD3SLmyMXMknstqKRe','
    系统管理员');
    insert into `t_user` (`id`, `username`, `password`, `name`)
    values('3','itcast','$2a$10$8qmaHgUFUAmPR5pOuWhYWOr291WJYjHelUlYn07k5ELF8ZCrW0Cui',
    'test02');
    insert into `t_user` (`id`, `username`, `password`, `name`)
    values('4','stu1','$2a$10$pLtt2KDAFpwTWLjNsmTEi.oU1yOZyIn9XkziK/y/spH5rftCpUMZa','学
    生1');
    insert into `t_user` (`id`, `username`, `password`, `name`)
    values('5','stu2','$2a$10$nxPKkYSez7uz2YQYUnwhR.z57km3yqKn3Hr/p1FR6ZKgc18u.Tvqm','学
    生2');
    insert into `t_user` (`id`, `username`, `password`, `name`)
    values('6','t1','$2a$10$TJ4TmCdK.X4wv/tCqHW14.w70U3CC33CeVncD3SLmyMXMknstqKRe','老师
    1');
    
    INSERT INTO `t_role` (`id`, `role_name`, `role_code`, `description`) VALUES('5','学
    生','student','学生');
    INSERT INTO `t_role` (`id`, `role_name`, `role_code`, `description`) VALUES('7','老
    师','teacher','老师');
    INSERT INTO `t_role` (`id`, `role_name`, `role_code`, `description`) VALUES('8','教
    学管理员','teachmanager','教学管理员');
    INSERT INTO `t_role` (`id`, `role_name`, `role_code`, `description`) VALUES('9','管
    理员','admin','管理员');
    INSERT INTO `t_role` (`id`, `role_name`, `role_code`, `description`) VALUES('10','超
    级管理员','super','超级管理员');
    
    INSERT INTO user_role(id,user_id,role_id) VALUES(NULL, '1', '5'),(NULL, '1', '7'),
    (NULL, '2', '8'),(NULL, '3', '9'),(NULL, '4', '8'),(NULL, '5', '10') ;
    

    --从三个表中查询满足要求的记录
    explain select * from t_role r, t_user u, user_role ur where r.id = ur.role_id and
    u.id = ur.user_id ;
    

    总结1:上面3个表的id一样的,但该SELECT语句实际操作表的顺序是从上到下,顺序为 角色表,用户角色的关联表,用户表。

    EXPLAIN SELECT * FROM t_role WHERE id = (SELECT role_id FROM user_role WHERE user_id
    = (SELECT id FROM t_user WHERE username = 'stu1'))
    

    总结2:id不同,值越大代表SQL语句优先操作这个表格。如上图中先操作用户表,然后操作用户角色表,最后操作角色表。

    EXPLAIN SELECT * FROM t_role r , (SELECT * FROM user_role ur WHERE ur.`user_id` =
    '2') a WHERE r.id = a.role_id ;
    

    总计3:id 有相同,也有不同,同时存在。id相同的可以认为是一组,从上往下顺序执行;在所有的组中,id的值越大,优先级越高,越先执行

    1-3-2 explain 之 select_type(查询的类型,是否包含子查询)
    select_type 含义
    SIMPLE 简单的select查询,查询中不包含子查询或者UNION
    PRIMARY 查询中若包含任何复杂的子查询,最外层查询标记为该标识
    SUBQUERY 在SELECT 或 WHERE 列表中包含了子查询
    DERIVED 在FROM 列表中包含的子查询,被标记为 DERIVED(衍生) MYSQL会递归执行这些子查 询,把结果放在临时表中
    UNION 若第二个SELECT出现在UNION之后,则标记为UNION ; 若UNION包含在FROM子句的子 查询中,外层SELECT将被标记为 : DERIVED
    UNION RESULT 从UNION表获取结果的SELECT
    写法上简单到复杂:
    Simple -> Primary -> Subquery -> Derived -> Union -> Union result
    
    1-3-3 explain 之 type(查找类型(返回数据的方式),重要指标)
    • 官方全称: join type(连接类型)
    type 含义
    NULL MySQL不访问任何表,索引,直接返回结果
    system 表只有一行记录(等于系统表),这是const类型的特例,一般不会出现
    const 所查询的结果最多只有一条记录匹配(使用主键索引与唯一索引),在where语句中,如果我们通过主键的字段=常量,或者建立唯一索引的字段==常量进行查询,那么这个效率就是const
    eq_ref ref_eq 与 ref相比牛的地方是,它知道这种类型的查找结果集只有一个?什么情况下结果集只有一个呢!那便是使用了主键或者唯一性索引进行查找的情况,比如根据学号查找某一学校的一名同学,在没有查找前我们就知道结果一定只有一个,所以当我们首次查找到这个学号,便立即停止了查询。这种连接类型每次都进行着精确查询,无需过多的扫描,因此查找效率更高,当然列的唯一性是需要根据实际情况决定的
    ref 出现该连接类型的条件是: 查找条件列使用了索引而且不为主键和unique。其实,意思就是虽然使用了索引,但该索引列的值并不唯一,有重复。这样即使使用索引快速查找到了第一条数据,仍然不能停止,要进行目标值附近的小范围扫描。但它的好处是它并不需要扫全表,因为索引是有序的,即便有重复值,也是在一个非常小的范围内扫描。
    range range指的是有范围的索引扫描,相对于index的全索引扫描,它有范围限制,因此要优于index。关于range比较容易理解,需要记住的是出现了range,则一定是基于索引的。同时除了显而易见的between,and以及'>','<'外,in和or也是索引范围扫描。
    index index 与 ALL的区别为 index 类型只是遍历了索引树, 通常比ALL 快, ALL 是遍历数据文件
    all 遍历全表以找到匹配的行

    上面的好坏程度排序如下

    • 要确保所写的语句有range级别的效率
      效率从高到底:
    system > const > eq_ref > ref > range > index > ALL
    
    1-3-4 explain 之 extra
    extra 含义
    using filesort 说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取, 称为 “文件排序”, 效率低
    using temporary 使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于 order by 和 group by; 效率低
    using index 表示相应的select操作使用了覆盖索引避免访问表的数据行, 效率不错
    explain总结
    • explain这个命令可以来查看SQL语句的执行计划,查看该SQL语句有没有使用上了索引,有没有做全表扫描。为SQL的语句优化提供指导

    1-4 在MYSQL中使用show profile 分析语句耗时

    基本知识点
    select @@have_profiling;      --查看系统变量,判断该功能是否开启
    set profiling=1;              --设置系统变量,打开功能
    

    profile的使用:

    show profiles;                       --显示profiles
    show profile for query id;           --实现指定id的profile的详细信息
    

    • 上图中Sending data 状态表示MySQL线程开始访问数据行并把结果返回给客户端

    1-5 在MYSQL中使用trace分析数据库的优化器执行计划

    优化器:MYSQL体系结构中服务层有一个组件叫做optimizer.

    使用方法:开启后,查看information_schema.optimizer_trace表格的内容,即为执行计划。

    --开启MYSQL的optimizer功能
    SET optimizer_trace="enabled=on",end_markers_in_json=on;
    set optimizer_trace_max_mem_size=1000000;   
    --执行命令
    select * from tb_item where id < 4;
    --查看optimizer的执行计划
    select * from information_schema.optimizer_traceG;
    

    参考资料:

    01 MYSQL中的约束(很好的文章)

    02 Mysql中key 、primary key 、unique key 与index区别

    03 MySQL explain的type,官方手册的中文解读(好文章)

    04 MYSQL的官方手册关于explain的部分(强烈推荐)

    05 数据库课程


    20210304

  • 相关阅读:
    053-146
    053-659
    053-658
    053-657
    053-656
    053-654
    053-655
    053-652
    053-651
    053-650
  • 原文地址:https://www.cnblogs.com/kfcuj/p/14481935.html
Copyright © 2011-2022 走看看