Skip to content

索引-使用原则

联合索引保证最左前缀法则

如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。

  • 若不包含索引中最左侧的字段,则索引全部失效
  • 如果跳跃某一列,索引将部分失效(条做的字段后面的字段索引失效)。
  • 最左前缀法则与查询时候 where 中字段书写的顺序无关,但是这个字段必须存在

示例:

sql
-- 下面的sql,因为跳过了age, 会导致status的索引失效
explain select from tb_user where profession='软件工程' and status='0';

-- 下列的两条sql不包含profession,不符合最左前缀法则, 会导致索引失效
explain select from tb_user where age=31 and status='0';
explain select from tb_user where status ='0';

联合索引范围查询不用><

联合索引中,出现范围查询(><)范围查询右侧的列索引失效, 使用大于等于或者小于等于就可以规避索引失效

示例:

注:tb_user 表的联合索引顺序为 profession、age、status

sql
-- 分析:下列的 sql 语句age字段出现范围查询,会导致 status 的索引失效
explain select*from tb user where profession='软件工程'and status='0' and age>30 :

-- 使用大于等于或小于等于就能规避
explain select*from tb_user where profession='软件工程' and status='0' and age>=30;

不要在索引列上运算

不要在索引列上进行运算操作,否则索引将失效

示例:

注:tb_user 表的 phone 字段建立了常规索引

sql
-- 查询手机号最后两位为15的用户,由于这里对phone进行字符串截取的运算,导致索引失效,全表扫描
explain select * from tb_user where substring(phone,10,2)='15';

字符串要加引号

字符串类型字段使用时,不加引号,索引将失效。

示例:

注:tb_user 表的联合索引顺序为 profession、age、status

sql
-- 由于status字段为char(1),但是在写sql时候 0 没加引号,导致 status没走索引
explain select * from tb_user where profession='软件工程' and age=31 and status=0;

-- phone为varchar类型,但是查询时候值没有引号导致索引失效
explain select * from tb_user where phone=17799990015;

模糊查询不要有前模糊匹配

  • 如果仅仅是尾部模糊匹配(xxx%),索引不会失效。
  • 只要有头部模糊匹配(%xxx%xx%),索引失效。

or连接的条件

or分割开的条件,将会遵循下面几条原则:

  • or一侧的条件有索引,另一侧的条件无索引,则整个的索引都不会被用到

数据分布影响

如果 MySQL 评估使用索引比全表更慢,则不使用索引。

例如:

要查询 phone 大于 17799990005 的数据,mysql 发现,有一半以上的数据都是符合条件的,走全表扫描还要快一写,那么即使 phone 有索引,也不会走索引

sql
select from tb_user where phone >='17799990005';

SQL 提示

  • 是什么? 在 SQL 语句中加入一些人为的提示来达到优化操作的目的

sql 提示是优化数据库的一个重要手段

  • 使用场景示例:

一个表中字段 A B C 已经创建了一个连个索引 A_B_C 了,还单独对字段 A 创建一个索引,那么 Mysql 在执行 select 语句查询字段 A 时候,会优先走哪个索引,mysql 会自动优化,若是我们硬想手动指定一个索引让 mysql 走这个索引,就要用到 sql 提示;

  • 语法
  1. use index:告诉数据库,用那个索引(仅建议,mysql 接不接受还得它权衡)
sql
explain select * from 表名 use index(索引名字) where 条件;
  1. ignore index:告诉数据库,不要用哪个索引
sql
explain select * from 表名 ignore index(索引名字) where 条件;
  1. force index:告诉数据库,必须走这个索引
sql
explain select * from 表名 force index(索引名字) where 条件;

TIP

三个 sql 提示的用法都是在 表名后 后加上 use/ignore/force index(索引名字)

尽量使用覆盖索引

概念

查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到,查询返回的字段全部都能对应上查询使用到的索引

提示

尽量使用覆盖索引,减少 select * 的使用。因为很容易出现回表查询导致性能降低

知识小贴士

若是 SQL 执行计划的结果中,Extra(额外信息)列中出现如下信息:

  • using index condition:查找使用了索引,但是需要回表查询数据
  • using where;using index:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据

示例:

前缀索引

当字段类型为字符串(varchar,text 等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘 IO,影响查 询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。

  • 语法
sql
-- 字段名(n)表示要截取这一个字段的前几个字符作为索引
create index 索引名称 on 表名(字段名(n));
  • 前缀长度

可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高, 唯一索引的选择性是 1,这是最好的索引选择性,性能也是最好的。

sql
-- 查询当前表里的email字段选择性
select count(distinct email)/count(*) from tb_user;

-- 截取email前5个字符在查询截取的字符的选择性
select count(distinct substring(email,1,5))/count(*) from tb_user

单列索引与联合索引

  • 单列索引:即一个索引只包含单个列。
  • 联合索引:即一个索引包含了多个列。

提示

在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引,而非单列索引。

单列索引情况:

sql
explain select id,phone,name from tb_user where phone='17799990010'and name ="韩信";

单列索引在查询的字段没在索引内时,会存在回表现象,下图为 mysql 自己的选择,我们可以手动指定联合索引

多条件联合查询时,MySQL 优化器会评估哪个字段的索引效率更高,会选择该索引完成本次查询。(多个索引只会选择一个)

索引设计原则

  1. 针对于数据量较大(一般一张表数据量超过100W就应该考虑建立索引),且查询比较频繁的表建立索引。

  2. 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。

  3. 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。

  4. 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。

  5. 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。

  6. 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。

  7. 如果索引列不能存储 NULL 值,请在创建表时使用 NOT NULL 约束它。当优化器知道每列是否包含 NULL 值时,它可以更好地确定哪个索引最有效地用于查询。