MySQL索引二

序言

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

  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索引二)