MySQL索引优化

不使用索引的情况

  1. 如果 mysql 估计使用索引比全表扫描更慢,则不使用索引。
    例如:如果 key_part 1 均匀分布在 1 和 100 之间,下列查询中使用索引就不是很好:
1
SELECT * FROM table_name where key_part1 > 1 and key_part1 < 90;
  1. 如果使用 heap 表并且 where 条件中不用=索引列,其他 > 、 < 、 >= 、 <= 均不使 用索引(MyISAM 和 innodb 表使用索引);

  2. 使用 or 分割的条件,如果 or 前的条件中的列有索引,后面的列中没有索引,那么涉及到的索引都不会使用。

  3. 如果创建复合索引,如果条件中使用的列不是索引列的第一部分;(不是前缀索引)

  4. 如果 like 是以%开始;

  5. 对 where 后边条件为字符串的一定要加引号,字符串如果为数字 mysql 会自动转 为字符串,但是不使用索引。

查看索引使用情况

如果索引正在工作, Handler_read_key 的值将很高,这个值代表了一个行被索引值读的次数,很低的值表明增加索引得到的性能改善不高,因为索引并不经常使 用。

Handler_read_rnd_next 的值高则意味着查询运行低效,并且应该建立索引补救。这个值的含义是在数据文件中读下一行的请求数。如果你正进行大量的表扫描,

该值较高。通常说明表索引不正确或写入的查询没有利用索引。

语法:

1
show status like 'Handler_read%';

具体优化查询语句

  1. 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,建议使用默认值
1
2
3
4
select id from t where num is null;

# 可以在 num 上设置默认值 0,确保表中 num 列没有 null
select id from t where num=0;
  1. 应尽量避免在 where 子句中使用!=或<>操作符

MySQL 只有对以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些时候的 LIKE。

可以在 LIKE 操作中使用索引的情形是指另一个操作数不是以通配符(%或者_)开头的情形。例如:

1
2
3
SELECT id FROM t WHERE col LIKE 'Mich%'; # 这个查询将使用索引,

SELECT id FROM t WHERE col LIKE '%ike'; #这个查询不会使用索引。
  1. 应尽量避免在 where 子句中使用 or 来连接条件,所有的 or 条件都必须是独立索引
1
2
3
4
select id from t where num=10 or num=20;
# 可以使用 UNION 合并查询:

select id from t where num=10 union all select id from t where num=20;
  1. in 和 not in 也要慎用
1
2
3
4
select id from t where num in(1,2,3);
对于连续的数值,能用 between 就不要用 in 了:

Select id from t where num between 1 and 3;
  1. 如果在 where 子句中使用参数,也会导致全表扫描。

因为 SQL 只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推 迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:

1
2
3
select id from t where num=@num;
# 可以改为强制查询使用索引
select id from t with(index(索引名)) where num=@num;
  1. 应尽量避免在 where 子句中对字段进行表达式操作
1
2
3
select id from t where num/2=100;
# 应改为:
select id from t where num=100\*2;
  1. 应尽量避免在 where 子句中对字段进行函数操作
1
2
3
4
5
6
select id from t where substring(name,1,3)='abc'; --name
select id from t where datediff(day,createdate,'2005-11-30')=0; --‘2005-11-30’
# 应改为:

select id from t where name like 'abc%';
select id from t where createdate>='2005-11-30' and createdate<'2005-12-1';
  1. 很多时候用 exists 代替 in 是一个好的选择:
1
2
3
4
select num from a where num in(select num from b);
用下面的语句替换:

select num from a where exists(select 1 from b where num=a.num);
  1. 并不是所有索引对查询都有效,

SQL 是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL 查询可能不会去利用索引,如一表中有字段 sex,male、female 几乎各一半,那么即使在 sex 上建了索引也对查询效率起不了作用。

  1. 索引并不是越多越好,

索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过 6 个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。