zoukankan      html  css  js  c++  java
  • MySQL的explain语句分析

    +----+-------------+-------+------------+------+---------------+-----+---------+------+------+----------+-------+
    | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
    +----+-------------+-------+------------+------+---------------+-----+---------+------+------+----------+-------+

    id

    select 查询的序列号,包含一组可以重复的数字,表示查询中执行sql语句的顺序。一般有三种情况:
    第一种:id全部相同,sql的执行顺序是由上至下;
    第二种:id全部不同,sql的执行顺序是根据id大的优先执行;
    第三种:id既存在相同,又存在不同的。先根据id大的优先执行,再根据相同id从上至下的执行。
    select_type

    select 查询的类型,主要是用于区别普通查询,联合查询,嵌套的复杂查询
    simple:简单的select 查询,查询中不包含子查询或者union
    primary:查询中若包含任何复杂的子查询,最外层查询则被标记为primary
    subquery:在select或where 列表中包含了子查询
    derived:在from列表中包含的子查询被标记为derived(衍生)MySQL会递归执行这些子查询,把结果放在临时表里。
    union:若第二个select出现在union之后,则被标记为union,若union包含在from子句的子查询中,外层select将被标记为:derived
    union result:从union表获取结果的select
    partitions

    表所使用的分区,如果要统计十年公司订单的金额,可以把数据分为十个区,每一年代表一个区。这样可以大大的提高查询效率。
    type

    这是一个非常重要的参数,连接类型,常见的有:all , index , range , ref , eq_ref , const , system , null 八个级别。
    性能从最优到最差的排序:system > const > eq_ref > ref > range > index > all
    对java程序员来说,若保证查询至少达到range级别或者最好能达到ref则算是一个优秀而又负责的程序员。
    all:(full table scan)全表扫描无疑是最差,若是百万千万级数据量,全表扫描会非常慢。
    index:(full index scan)全索引文件扫描比all好很多,毕竟从索引树中找数据,比从全表中找数据要快。
    range:只检索给定范围的行,使用索引来匹配行。范围缩小了,当然比全表扫描和全索引文件扫描要快。sql语句中一般会有between,in,>,< 等查询。
    ref:非唯一性索引扫描,本质上也是一种索引访问,返回所有匹配某个单独值的行。比如查询公司所有属于研发团队的同事,匹配的结果是多个并非唯一值。
    eq_ref:唯一性索引扫描,对于每个索引键,表中有一条记录与之匹配。比如查询公司的CEO,匹配的结果只可能是一条记录,
    const:表示通过索引一次就可以找到,const用于比较primary key 或者unique索引。因为只匹配一行数据,所以很快,若将主键至于where列表中,MySQL就能将该查询转换为一个常量。
    system:表只有一条记录(等于系统表),这是const类型的特列,平时不会出现,了解即可
    possible_keys

    显示查询语句可能用到的索引(一个或多个或为null),不一定被查询实际使用。仅供参考使用。
    key

    显示查询语句实际使用的索引。若为null,则表示没有使用索引。
    key_len

    显示索引中使用的字节数,可通过key_len计算查询中使用的索引长度。在不损失精确性的情况下索引长度越短越好。key_len 显示的值为索引字段的最可能长度,并非实际使用长度,即key_len是根据表定义计算而得,并不是通过表内检索出的。
    ref

    显示索引的哪一列或常量被用于查找索引列上的值。
    rows

    根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数,值越大越不好。
    extra

    Using filesort: 说明MySQL会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作称为“文件排序” 。出现这个就要立刻优化sql。
    Using temporary: 使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序 order by 和 分组查询 group by。 出现这个更要立刻优化sql。
    Using index: 表示相应的select 操作中使用了覆盖索引(Covering index),避免访问了表的数据行,效果不错!如果同时出现Using where,表明索引被用来执行索引键值的查找。如果没有同时出现Using where,表示索引用来读取数据而非执行查找动作。
    覆盖索引(Covering Index) :也叫索引覆盖,就是select 的数据列只用从索引中就能够取得,不必读取数据行,MySQL可以利用索引返回select 列表中的字段,而不必根据索引再次读取数据文件。
    Using index condition: 在5.6版本后加入的新特性,优化器会在索引存在的情况下,通过符合RANGE范围的条数 和 总数的比例来选择是使用索引还是进行全表遍历。
    Using where: 表明使用了where 过滤
    Using join buffer: 表明使用了连接缓存
    impossible where: where 语句的值总是false,不可用,不能用来获取任何元素
    distinct: 优化distinct操作,在找到第一匹配的元组后即停止找同样值的动作。
    filtered

    一个百分比的值,和rows 列的值一起使用,可以估计出查询执行计划(QEP)中的前一个表的结果集,从而确定join操作的循环次数。小表驱动大表,减轻连接的次数。

    通过explain的参数介绍,我们可以得知:
    1 表的读取顺序(id)
    2 数据读取操作的操作类型(type)
    3 哪些索引被实际使用(key)
    4 表之间的引用(ref)
    5 每张表有多少行被优化器查询(rows)

    索引分类

    我们常说的索引一般指的是BTree(多路搜索树)结构组织的索引。其中还有聚合索引,次要索引,复合索引,前缀索引,唯一索引,统称索引,当然除了B+树外,还有哈希索引(hash index)等。

    1.普通索引index :加速查找
    2.唯一索引
    主键索引:primary key :加速查找+约束(不为空且唯一)
    唯一索引:unique:加速查找+约束 (唯一)
    3.联合索引
    -primary key(id,name):联合主键索引
    -unique(id,name):联合唯一索引
    -index(id,name):联合普通索引
    4.全文索引fulltext :用于搜索很长一篇文章的时候,效果最好。
    5.空间索引spatial :了解就好,几乎不用

    基本语法:
    创建:

    create [unique] index indexName on tableName (columnName...)
    alter tableName add [unique] index [indexName] on (columnName...)

    删除:

    drop index [indexName] on tableName

    查看:

    show index from tableName
    哪些情况需要建索引:
    1 主键,唯一索引
    2 经常用作查询条件的字段需要创建索引
    3 经常需要排序、分组和统计的字段需要建立索引
    4 查询中与其他表关联的字段,外键关系建立索引

    哪些情况不要建索引:
    1 表的记录太少,百万级以下的数据不需要创建索引
    2 经常增删改的表不需要创建索引
    3 数据重复且分布平均的字段不需要创建索引,如 true,false 之类。
    4 频发更新的字段不适合创建索引
    5 where条件里用不到的字段不需要创建索引

    注意:

    1、索引需要占用磁盘空间,因此在创建索引时要考虑到磁盘空间是否足够

    2、创建索引时需要对表加锁,因此实际操作中需要在业务空闲期间进行

    1.最左前缀匹配原则,非常重要的原则,

    create index ix_name_email on s1(name,email,)

    • 最左前缀匹配:必须按照从左到右的顺序匹配
      select * from s1 where name='egon'; #可以
      select * from s1 where name='egon' and email='asdf'; #可以
      select * from s1 where email='alex@oldboy.com'; #不可以

    mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,

    比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,
    d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

    2.=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器

    会帮你优化成索引可以识别的形式

    3.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),

    表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、
    性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,
    这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录

    4.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’

    就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,
    但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。
    所以语句应该写成create_time = unix_timestamp(’2014-05-29’);

    索引无法命中的情况:

    • like '%xx'
      select * from tb1 where email like '%cn';

    • 使用函数
      select * from tb1 where reverse(email) = 'wupeiqi';

    • or
      select * from tb1 where nid = 1 or name = 'seven@live.com';

      特别的:当or条件中有未建立索引的列才失效,以下会走索引
      select * from tb1 where nid = 1 or name = 'seven';
      select * from tb1 where nid = 1 or name = 'seven@live.com' and email = 'alex'

    • 类型不一致
      如果列是字符串类型,传入条件是必须用引号引起来,不然...
      select * from tb1 where email = 999;

    普通索引的不等于不会走索引

    • !=
      select * from tb1 where email != 'alex'

      特别的:如果是主键,则还是会走索引
      select * from tb1 where nid != 123

    • select * from tb1 where email > 'alex'

      特别的:如果是主键或索引是整数类型,则还是会走索引
      select * from tb1 where nid > 123
      select * from tb1 where num > 123

    排序条件为索引,则select字段必须也是索引字段,否则无法命中

    • order by
      select name from s1 order by email desc;
      当根据索引排序时候,select查询的字段如果不是索引,则不走索引
      select email from s1 order by email desc;
      特别的:如果对主键排序,则还是走索引:
      select * from tb1 order by nid desc;

    • 组合索引最左前缀
      如果组合索引为:(name,email)
      name and email -- 使用索引
      name -- 使用索引
      email -- 不使用索引

    • count(1)或count(列)代替count(*)在mysql中没有差别了

    • create index xxxx on tb(title(19)) #text类型,必须制定长度

    • 避免使用select *

    • count(1)或count(列) 代替 count(*)

    • 创建表时尽量时 char 代替 varchar

    • 表的字段顺序固定长度的字段优先

    • 组合索引代替多个单列索引(经常使用多个条件查询时)

    • 尽量使用短索引

    • 使用连接(JOIN)来代替子查询(Sub-Queries)

    • 连表时注意条件类型需一致

    • 索引散列值(重复少)不适合建索引,例:性别不适合

  • 相关阅读:
    Java暑期学习第三十一天日报
    使用dataadapter和dataset更新数据库
    一些很酷的.Net技
    一个阴历阳历互相转化的类(c#农历)
    详细讲解简洁、明晰!数据库设计三大范式应用实例
    WinForm下ListBox控件“设置DataSource属性后无法修改项集合”的问题解决方案
    CONVERT函数方法大全(经验总结)
    使用C#将字符串转换成十六进制以及其逆转换
    List myList = new List()有关的方法及属性
    C#实现P2P之UDP穿透NAT及其原理讲解11
  • 原文地址:https://www.cnblogs.com/sanduzxcvbnm/p/11640172.html
Copyright © 2011-2022 走看看