MySQL索引优化
不使用索引的情况
- 如果 mysql 估计使用索引比全表扫描更慢,则不使用索引。
例如:如果 key_part 1 均匀分布在 1 和 100 之间,下列查询中使用索引就不是很好:
1 | SELECT * FROM table_name where key_part1 > 1 and key_part1 < 90; |
如果使用 heap 表并且 where 条件中不用=索引列,其他 > 、 < 、 >= 、 <= 均不使 用索引(MyISAM 和 innodb 表使用索引);
使用 or 分割的条件,如果 or 前的条件中的列有索引,后面的列中没有索引,那么涉及到的索引都不会使用。
如果创建复合索引,如果条件中使用的列不是索引列的第一部分;(不是前缀索引)
如果 like 是以%开始;
对 where 后边条件为字符串的一定要加引号,字符串如果为数字 mysql 会自动转 为字符串,但是不使用索引。
查看索引使用情况
如果索引正在工作, Handler_read_key 的值将很高,这个值代表了一个行被索引值读的次数,很低的值表明增加索引得到的性能改善不高,因为索引并不经常使 用。
Handler_read_rnd_next 的值高则意味着查询运行低效,并且应该建立索引补救。这个值的含义是在数据文件中读下一行的请求数。如果你正进行大量的表扫描,
该值较高。通常说明表索引不正确或写入的查询没有利用索引。
语法:
1 | show status like 'Handler_read%'; |
具体优化查询语句
- 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,建议使用默认值
1 | select id from t where num is null; |
- 应尽量避免在 where 子句中使用!=或<>操作符
MySQL 只有对以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些时候的 LIKE。
可以在 LIKE 操作中使用索引的情形是指另一个操作数不是以通配符(%或者_)开头的情形。例如:
1 | SELECT id FROM t WHERE col LIKE 'Mich%'; # 这个查询将使用索引, |
- 应尽量避免在 where 子句中使用 or 来连接条件,所有的 or 条件都必须是独立索引
1 | select id from t where num=10 or num=20; |
- in 和 not in 也要慎用
1 | select id from t where num in(1,2,3); |
- 如果在 where 子句中使用参数,也会导致全表扫描。
因为 SQL 只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推 迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
1 | select id from t where num=@num; |
- 应尽量避免在 where 子句中对字段进行表达式操作
1 | select id from t where num/2=100; |
- 应尽量避免在 where 子句中对字段进行函数操作
1 | select id from t where substring(name,1,3)='abc'; --name |
- 很多时候用 exists 代替 in 是一个好的选择:
1 | select num from a where num in(select num from b); |
- 并不是所有索引对查询都有效,
SQL 是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL 查询可能不会去利用索引,如一表中有字段 sex,male、female 几乎各一半,那么即使在 sex 上建了索引也对查询效率起不了作用。
- 索引并不是越多越好,
索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过 6 个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。