MySQL 的查询缓存、执行计划 和 开启慢查询

MySQL 的查询缓存

工作原理

  • 缓存SELECT操作的结果集和SQL语句;
  • 新的SELECT语句,先去查询缓存,判断是否存在可用的记录集

触发缓存

  • 与缓存的SQL语句,是否完全一样,区分大小写
  • 简单认为存储了一个key-value结构,key为sql,value为sql查询结果集

缓存配置参数

show variables like 'query_cache%';

query_cache_type:

  • 0 -– 不启用查询缓存,默认值;
  • 1 -– 启用查询缓存,
    • 只要符合查询缓存的要求,客户端的查询语句和记录集都可以缓存起来,供其他客户端使用,
    • 加上 SQL_NO_CACHE将不缓存
  • 2 -– 启用查询缓存,
    • 只要查询语句中添加了参数:SQL_CACHE,且符合查询缓存的要求,客户端的查询语句和记录集,则可以缓存起来,供其他客户端使用

query_cache_size:

  • 允许设置query_cache_size的值最小为40K,默认1M,
  • 推荐设置 为:64M/128M;

query_cache_limit:

  • 限制查询缓存区最大能缓存的查询记录集,默认设置为1M

命令可查看缓存情况

show status like 'Qcache%';

不会缓存的情况

  • 当查询语句中有一些不确定的数据时,则不会被缓存。如包含函数NOW(),CURRENT_DATE()等类似的函数,或者用户自定义的函数,存储函数,用户变量等都不会被缓存
  • 当查询的结果大于query_cache_limit设置的值时,结果不会被缓存
  • 于InnoDB引擎来说,当一个语句在事务中修改了某个表,那么在这个事务提交之前,所有与这个表相关的查询都无法被缓存。因此长时间执行事务,会大大降低缓存命中率
  • 查询的表是系统表(select * from mysql.user;)
  • 查询语句不涉及到表(select 1;)

为什么mysql默认关闭了缓存开启??

  • 在查询之前必须先检查是否命中缓存,浪费计算资源
  • 如果这个查询可以被缓存,那么执行完成后,MySQL发现查询缓存中没有这个查询,则会将结果存入查询缓存,这会带来额外的系统消耗
  • 针对表进行写入或更新数据时,将对应表的所有缓存都设置失效。
  • 如果查询缓存很大或者碎片很多时,这个操作可能带来很大的系统消耗

使用业务场景

以读为主的业务,数据生成之后就不常改变的业务.
比如门户类、新闻类、报表类、论坛类等

执行计划

Mysql的查询优化器是基于成本计算的原则。他会尝试各种执行计划。数据抽样的方式进行试验

  • 使用等价变化规则:基于联合索引,调整条件位置等
    • 5 = 5 and a > 5 改写成 a > 5
    • a < b and a = 5 改写成 b > 5 and a = 5
  • 优化count 、min、max等函数
    • min函数只需找索引最左边
    • max函数只需找索引最右边
    • myisam引擎count(*)
  • 覆盖索引扫描
  • 子查询优化
  • 提前终止查询
    • 用了limit关键字或者使用不存在的条件
    • select * from user where id=-1;
  • IN的优化
    • 先进性排序,再采用二分查找的方式
    • 这里表述的是 in (1,2,3,4) ,而不是in (子查询)
    • in 中包含子查询的 写法,不建议使用,会导致驱动表全表扫描

如何查看执行计划

mysql> explain select * from person where id in(select id from person where name='guaoran') \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: person
   partitions: NULL
         type: ref
possible_keys: PRIMARY,idx_name
          key: idx_name
      key_len: 302
          ref: const
         rows: 1
     filtered: 100.00
        Extra: Using index
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: person
   partitions: NULL
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: my_demo.person.id
         rows: 1
     filtered: 100.00
        Extra: NULL
2 rows in set, 1 warning (0.00 sec)
mysql> explain select * from my_innodb where id in(select phonenum from my_archive where age=20) \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: <subquery2>
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
     filtered: 100.00
        Extra: Using where
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: my_innodb
   partitions: NULL
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 8
          ref: <subquery2>.phonenum
         rows: 1
     filtered: 100.00
        Extra: Using where
*************************** 3. row ***************************
           id: 2
  select_type: MATERIALIZED
        table: my_archive
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 999999
     filtered: 10.00
        Extra: Using where
3 rows in set, 1 warning (0.00 sec)

执行计划-id

select查询的序列号,标识执行的顺序

  1. id相同,执行顺序由上至下
  2. id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
  3. id相同又不同即两种情况同时存在,id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行

执行计划-select_type

查询的类型,主要是用于区分普通查询、联合查询、子查询等

  1. SIMPLE:简单的select查询,查询中不包含子查询或者union
  2. PRIMARY:查询中包含子部分,最外层查询则被标记为primary
  3. SUBQUERY/MATERIALIZED:
    1. SUBQUERY表示在select 或 where列表中包含了子查询
    2. MATERIALIZED表示where 后面in条件的子查询
  4. UNION:若第二个select出现在union之后,则被标记为union;
  5. UNION RESULT:从union表获取结果的select

执行计划-table

查询涉及到的表

  1. 直接显示表名或者表的别名
    1. <unionM,N> 由ID为M,N 查询union产生的结果
    2. <subqueryN> 由ID为N查询生产的结果

执行计划-type

访问类型,sql查询优化中一个很重要的指标,结果值从好到坏依次是: system > const > eq_ref > ref > range > index > ALL

  1. system:表只有一行记录(等于系统表),const类型的特例,基本不会出现,可以忽略不计

  2. const:表示通过索引一次就找到了,const用于比较primary key 或者 unique索引

       explain select * from my_innodb where id =1 \G
    
  3. eq_ref:唯一索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键 或 唯一索引扫描

        explain select * from users u,user_address a where a.userId = u.id\G
    
  4. ref:非唯一性索引扫描,返回匹配某个单独值的所有行,本质是也是一种索引访问

        explain select * from users where age=5\G
    
  5. range:只检索给定范围的行,使用一个索引来选择行

  6. index:Full Index Scan,索引全表扫描,把索引从头到尾扫一遍

    1.  explain select depart from person \G
      
  7. ALL:Full Table Scan,遍历全表以找到匹配的行

执行计划-possible_keys/key/rows/filtered

  1. possible_keys : 查询过程中有可能用到的索引
  2. key : 实际使用的索引,如果为NULL,则没有使用索引
  3. rows : 根据表统计信息或者索引选用情况,大致估算出找到所需的记录所需要读取的行数
  4. filtered : 它指返回结果的行占需要读到的行(rows列的值)的百分比。(有效率命中率)
    1. 表示返回结果的行数占需读取行数的百分比,filtered的值越大越好

执行计划-Extra

十分重要的额外信息

  1. Using filesort :
    mysql对数据使用一个外部的文件内容进行了排序,而不是按照表内的索引进行排序读取

    mysql> explain select * from cm_customer_person order by name desc \G
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: cm_customer_person
       partitions: NULL
             type: ALL
    possible_keys: NULL
              key: NULL
          key_len: NULL
              ref: NULL
             rows: 37009
         filtered: 100.00
            Extra: Using filesort
    1 row in set, 1 warning (0.00 sec)
    
  2. Using temporary:

    使用临时表保存中间结果,也就是说mysql在对查询结果排序时使用了临时表,常见于order by 或 group by

  3. Using index:

    表示相应的select操作中使用了覆盖索引(Covering Index),避免了访问表的数据行,效率高

  4. Using where :

    表示使用了where过滤条件

  5. select tables optimized away:

    基于索引优化MIN/MAX操作或者MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段在进行计算,查询执行计划生成的阶段即可完成优化

  6. Using index condition

    Using index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行;

使用慢查询

在 linux 上进行 慢查询分析 ,可以输出到 file 文件中和 数据库表中

## 查看慢查询的配置
show variables like 'slow_query_log';
## 开启慢查询日志
set global slow_query_log=on;
## 自定义设置慢查询日志文件
set global slow_query_log_file = '/guaoran/mysql/data/slow_query_log_demo.log';
## sql 查询要不要记录慢查询
set global log_queries_not_using_indexes=on;
## 单位 秒 ,当查询时间超过0.1s就记录慢查询日志
set global long_query_time = 0.1;
## 慢查询信息
show global status like '%slow%';
## 使用自带工具分析慢查询日志
mysqldumpslow -s c  /guaoran/mysql/data/slow_query_log_demo.log 
# perl mysqldumpslow -s c  /usr/local/mysql/data/localhost-slow.log 

## 查看所有操作的记录
show variables like '%general%';
## 打开、关闭记录所有的日志操作记录
set global general_log=on;
set global general_log=off;


## 查看慢查询日志输出的格式 该输出包含 慢日志查询和general_log 的输出
show variables like '%log_output%';
## 设置慢查询输出的 格式,table 的话,可以在mysql.slow_log 和 mysql.general_log 中查看
set global log_output='FILE,TABLE';

查询用时最多的20 条慢SQL

mysqldumpslow -s t -t 20 -g 'select' /var/lib/mysql/localhost-slow.log

Count 代表这个SQL 执行了多少次;
Time 代表执行的时间,括号里面是累计时间;
Lock 表示锁定的时间,括号是累计;
Rows 表示返回的记录数,括号是累计。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值