美文网首页
MySQL索引二

MySQL索引二

作者: betterFighter | 来源:发表于2020-04-11 14:58 被阅读0次

    序言

    在上一篇文章中主要讲了索引的底层实现。主要讨论了为什么加了索引以后,数据库的查询效率会加快?底层怎么实现的?这个问题。
    那么本文主要想讨论下以下几个问题

    1. 怎么建立合适索引,建立索引有哪些需要规避的要点?
    2. 为什么有时候建立了索引也没啥子用?
      以下是自己最近学习和使用的一些想法,希望大家可以互相交流,反正是技术交流哈哈。

    首先抛出两个语句干货。大家可以先去自己的数据库执行检查下。

    -- 查询冗余索引,看看是否有累赘索引

    SELECT * from sys.schema_redundant_indexes;

    -- 查询从未被使用到的索引,用都没用过的,留着做啥。

    SELECT * from sys.schema_unused_indexes;

    怎么建立合适索引,建立索引有哪些需要规避的要点

        首先我们要知道,索引并不是越多越好。虽然索引用的很爽。
        因为底层是B+树结构,我们在增,删,改操作中都会对B+树结构进行调整。
        所以索引过多也会影响此类操作。主要考虑在where和order by 后面涉及的列建立索引。
        同时,由于一条mysql在执行的时候,如果有多个索引命中,最终只能选择其中一个索引。
    

    所以个人见解:

    要点一:复合索引和单列索引相比,应优先使用复合索引。

        针对建立复合索引之前,需要先做一个调研把哪个字段作为最左前缀。
        调研可以从这几个方面去判断
        1.有哪个字段是经常会使用到的,且大部分sql中都会有值。
        举个例子:
        user表里有id,name,age,address,sex等字段。
        调研发现多数sql中都使用到了name这个字段作为查询条件。因此可建立复合索引idx_name_age_address(name,age,address)
        此处使用到的是mysql里最常见的最左原则。为什么会有这个原则,和底层原理有关,在上一篇文章中也叙述了。
        2.字段长度小的列放在左侧。因为字段长度小,每一页能存储的数据量就越大,IO性能越好,也就是越快找到目标数据。
        3.如果创建的复合索引中某个字段中的值都是不同的,那么他的数据区分度就高,走索引效率就非常明显,考虑放在左侧。
        这种复合索引比建立单列索引实用的多。
    

    要点二:值比较稀少的列不建议使用索引

        比如上面例子中的sex字段。换句话说,建了索引也对查询效率起不了作用。那么为啥会有这种论断。
        因为系统在执行一条sql的时候,会进行预测走这个索引和全表扫描哪个扫描的行数少。扫描行数越少,I/O操作次数越少。
        而我们走索引的时候,会通过sex这个索引先查到主键索引,再通过主键索引来查找数据。也就是会走两次索引。也就是回表。
        系统通过索引的区分度来判断,索引上不同的值越多,这个基数越大,那么走索引查询越有优势。
        
        如果我们业务需要强制走某个索引查询的话。可以使用select * from user force idx(age) where address = '中国'
    

    要点三:尽量建立覆盖索引

        所谓覆盖索引就是此索引覆盖所有需要查询的字段的值。尤其是查询频繁的语句,优先考虑覆盖索引。上述例子中
        建立了复合索引idx_name_age_address(name,age,address)
        查询语句:select id,name from user where name = '张三' and age = '20' and address = '中国'
        因为B+树的叶子节点存储的是主键+列值,最终还是要回表,就会比较慢。但覆盖索引要查询出的列和索引是对应的,不需要做回表操作。
        我们往往会因为偷懒或者想让sql能够复用,而使用select * from 的写法。但这种无法使用覆盖索引,也会消耗更多的CPU和IO
    

    判断建立的索引是否有效

    要点一:判断是否符合了最左前缀原则。

    以上述例子中为例:
        建立了复合索引idx_name_age_address(name,age,address)
        查询语句:select * from user where  age = '20' and address = '中国'  
        此语句因为最左前缀为name,但是name没有作为条件查询,无法使用索引。
    

    要点二:查看where 子句左边是否有进行函数,算术运算或者其他表达式运算

        select * from user where  age+1 = 20  -- 不能使用索引
        select * from user where  age = 20 -1 -- 能使用索引
    

    要点三:尽量避免在WHERE子句中使用!=或<>操作符,将打算加索引的列设置为 NOT NULL。否则将导致引擎放弃使用索引而进行全表扫描。

    要点四:查询语句有多个索引,数据库选错索引。

    数据库使用采样的方式来预测各个索引的基数。既然是采样,就有可能失误。
    若系统判断当前索引基数过小,就不走索引,直接全表扫描。
    所以不要对每个单列建立索引。索引需要的是有效,而不是多。
    

    要点五:联合索引的第一个索引使用了范围查找导致失效

    以上述例子中为例:
         建立了复合索引idx_name_age_address(name,age,address)
        查询语句:select * from user where  name in ('张三',‘小敏’) and age = '20' and address = '中国'  
        在此sql中,name字段会用到索引,但是后面的age和address索引失效。
        因为一个 SQL 只能利用复合索引中的一列进行范围查询。
        所以如果有范围查询的字段可以放在复合索引的右边。不要使用not in 
        not in 通常会让索引失效,可以用left join 或者 not exist 替代。
    

    要点六:排查是否使用了子查询

    尽量不使用子查询,可用join替代。
    因为子查询的结果集会存储在临时表中,而临时表是没有索引的。同时也会消耗过多的CPU和IO。也是慢sql的一部分原因。
    

    总结

        其实mysql使用B+树作为自己的数据结构,它是非常强大的。千万数据量的大表,如果索引能够使用的合适,也完全能够支撑。
    索引的重要性不言而喻,甚至在做开发设计评审时,索引也应该作为数据库设计里重要的一项进行评审。
    如果文中有说的不正确的地方,希望大家能够互相交流,一起进步。

    相关文章

      网友评论

          本文标题:MySQL索引二

          本文链接:https://www.haomeiwen.com/subject/zmiymhtx.html