Mysql索引使用、索引优化(二)

一、什么情况下适合使用索引?

  1. 字段的数值有唯一性的限制,比如用户名,可以用唯一索引或者主键索引
  2. 频繁作为 WHERE 查询条件的字段,尤其在数据表大的情况下
  3. 需要经常 GROUP BY 和 ORDER BY 的列
    1. 索引就是让数据按照某种顺序进行存储或检索,因此当我们使用 GROUP BY 对数据进行分组查询,或者使用 ORDER BY 对数据进行排序的时候,就需要对分组或者排序的字段进行索引。
    2. 如果我们需要对不同字段同时进行 GROUP BY 和 ORDER BY,那么是不是需要单独创建不同的索引呢?
      当我们对 user_id 和 comment_time 分别创建索引,执行下面的 SQL 查询:
SELECT user_id, count(*) as num FROM product_comment group by user_id order by comment_time desc limit 100

运行结果(运行时间 >100s):
实际上多个单列索引在多条件查询时只会生效一个索引(MySQL 会选择其中一个限制最严格的作为索引),所以在多条件联合查询的时候最好创建联合索引。
我们创建联合索引 (user_id, comment_time),再来看下查询的时间,查询时间为 0.775s
如果我们创建联合索引的顺序为 (comment_time, user_id) 呢?运行时间为 1.990s,
这是因为在进行 SELECT 查询的时候,先进行 GROUP BY,再对数据进行 ORDER BY 的操作,所以按照这个联合索引的顺序效率是最高的。

  1. UPDATE、DELETE 的 WHERE 条件列,一般也需要创建索引
  2. DISTINCT 字段需要创建索引
  3. 做多表 JOIN 连接操作时,创建索引需要注意以下的原则
    1. 首先,连接表的数量尽量不要超过 3 张,因为每增加一张表就相当于增加了一次嵌套的循环,数量级增长会非常快,严重影响查询的效率。
    2. 其次,对 WHERE 条件创建索引,因为 WHERE 才是对数据条件的过滤。如果在数据量非常大的情况下,没有 WHERE 条件过滤是非常可怕的。
    3. 最后,对用于连接的字段创建索引,并且该字段在多张表中的类型必须一致。比如 user_id 在 product_comment 表和 user 表中都为 int(11) 类型,而不能一个为 int 另一个为 varchar 类型。

二、 什么情况下不适合使用索引?

  1. 在数据表中的数据行数比较少的情况下,比如不到 1000 行,是不需要创建索引的
  2. 当数据重复度大,比如高于 10% 的时候,也不需要对这个字段使用索引 ,如果是性别这个字段,就不需要对它创建索引。
  3. WHERE 条件(包括 GROUP BY、ORDER BY)里用不到的字段不需要创建索引,索引的价值是快速定位,如果起不到定位的字段通常是不需要创建索引的。
  4. 频繁更新的字段不一定要创建索引。因为更新数据的时候,也需要更新索引,如果索引太多,在更新索引的时候也会造成负担,从而影响效率。

三、什么情况下索引失效?

  1. 如果索引进行了表达式计算,则会失效
EXPLAIN SELECT comment_id, user_id, comment_text FROM product_comment 
WHERE comment_id+1 = 900001

-- 解决 :重写sql
SELECT comment_id, user_id, comment_text FROM product_comment 
WHERE comment_id = 900000

  1. 如果对索引使用函数,也会造成失效
-- 对 comment_text 的前三位为 abc 的内容进行条件筛选
EXPLAIN SELECT comment_id, user_id, comment_text FROM product_comment 
WHERE SUBSTRING(comment_text, 1,3)='abc'

-- 解决 :重写sql
SELECT comment_id, user_id, comment_text FROM product_comment 
WHERE comment_text LIKE 'abc%'

  1. 在 WHERE 子句中,如果在 OR 前的条件列进行了索引,而在 OR 后的条件列没有进行索引,那么索引会失效。
-- comment_id 是主键,而 comment_text 没有进行索引,因为 OR 的含义就是两个只要满足一个即可,
-- 因此只有一个条件列进行了索引是没有意义的,只要有条件列没有进行索引,就会进行全表扫描
EXPLAIN SELECT comment_id, user_id, comment_text FROM product_comment 
WHERE comment_id = 900001 OR comment_text = '462eed7ac6e791292a79'

-- 解决 :对comment_text 创建索引
  1. . 当我们使用 LIKE 进行模糊查询的时候,后面不能是 %
EXPLAIN SELECT comment_id, user_id, comment_text FROM product_comment
 WHERE comment_text LIKE '%abc'
  1. 索引列与 NULL 或者 NOT NULL 进行判断的时候也会失效。
-- 这是因为索引并不存储空值,所以最好在设计数据表的时候就将字段设置为 NOT NULL 约束,
-- 比如你可以将 INT 类型的字段,默认值设置为 0。将字符类型的默认值设置为空字符串 (’’)。
  1. 我们在使用联合索引的时候要注意最左原则,
    假设我们有 x、y、z 三个字段,创建联合索引(x, y, z)之后,我们可以把 x、y、z 分别类比成“百分位”、“十分位”和“个位”。

    查询“x=9 AND y=8 AND z=7”的过程,就是在一个由小到大排列的数值序列中寻找“987”,可以很快找到。

    查询“y=8 AND z=7”,就用不上索引了,因为可能存在 187、287、387、487………这样就必须扫描所有数值。

    查询“z=7 AND y=8 AND x=9”的时候,如果三个字段 x、y、z 在条件查询的时候是乱序的,但采用的是等值查询(=)或者是 IN 查询,那么 MySQL 的优化器可以自动帮我们调整为可以使用联合索引的形式。

    当我们查询“x=9 AND y>8 AND z=7”的时候,如果建立了 (x,y,z) 顺序的索引,这时候 z 是用不上索引的。这是因为 MySQL 在匹配联合索引最左前缀的时候,如果遇到了范围查询,比如(<)(>)和 between 等,就会停止匹配。索引列最多作用于一个范围列,对于后面的 Z 来说,就没法使用到索引了。

    通过这个我们也可以知道,联合索引的最左前缀匹配原则针对的是创建的联合索引中的顺序,如果创建了联合索引(x,y,z),那么这个索引的使用顺序就很重要了。如果在条件语句中只有 y 和 z,那么就用不上联合索引。

    此外,SQL 条件语句中的字段顺序并不重要,因为在逻辑查询优化阶段会自动进行查询重写。

    最后你需要记住,如果我们遇到了范围条件查询,比如(<)(<=)(>)(>=)和 between 等,那么范围列后的列就无法使用到索引了。

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章