目录
一、查看索引使用情况
mysql> show status like 'handler_read%';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 0 |
| Handler_read_key | 0 |
| Handler_read_last | 0 |
| Handler_read_next | 0 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 16 |
+-----------------------+-------+
Handler_read_key #这个值越高越好,越高表示使用索引查询到的次数
Handler_read_rnd_next #这个值越高,说明查询低效
二、 索引的使用技巧
1、搜索的索引列,不一定是所要选择的列。
换句话说,最适合索引的列是出现在 WHERE 子句中的列,或连接子句中指定的列,而不是出现在 SELECT 关键字后的选择列表中的列。
select user_id from user;
#不是因为要搜索user_id而加索引,而是为了后面的where 条件加索引,不会出现在where 子句中的字段不该创建索引
2、使用唯一索引。
考虑某列中值的分布。对于惟一值的列,索引的效果最好,而具有多个重复值的列,其索引效果最差。
例如,存放年龄的列具有不同值,很容易区分 各行。 而用来记录性别的列,只含有“ M” 和“F” ,则对此列进行索引没有多大用处(不管 搜索哪个值,都会得出大约一半的行)
列值唯一性太差的不适合单独创建索引
3、较频繁的作为查询字段应该创建索引
4、使用短索引。
如果对字符串列进行索引,应该指定一个前缀长度,只要有可能就应该这样做。
例如,如果有一个 CHAR(200) 列,如果在前 10 个或 20 个字符内,多数值是惟一的, 那么就不要对整个列进行索引。对前 10 个或 20 个字符进行索引能够节省大量索引空 间,也可能会使查询更快。较小的索引涉及的磁盘 I/O 较少,较短的值比较起来更快。
更为重要的是,对于较短的键值,索引高速缓存中的块能容纳更多的键值,因此, MySQL也可以在内存中容纳更多的值。这增加了找到行而不用读取索引中较多块的可能性。
(当然,应该利用一些常识。如仅用列值的第一个字符进行索引是不可能有多大好处的, 因为这个索引中不会有许多不 同的值。)
5、利用最左前缀。
在创建一个 n 列的索引时,实际是创建了 MySQL 可利用的 n 个索引。
多列索引可起几个索引的作用,因为可利用索引中最左边的列集来匹配行。这样的列集称为最左前缀。(这与索引一个列的前缀不同,索引一个列的前缀是利用该列的前 n 个字符作为索引值。)
6、不要过度索引。
不要以为索引“越多越好” ,什么东西都用索引是错的。
每个额外的索引都要占用额外的磁盘空间,并降低写操作的性能, 修改表的内容时,索引必须进行更新,有时可能需要重构,因此,索引越多,所花的时间越长。如果有一个索引很少利用或从不使用,那么会不必要地减缓表的修改速度。
此外, MySQL 在生成一个执行计划时,要考虑各个索引,这也要费时间。创建多余的索引给查询优化带来了更多的工作。索引太多,也可能会使 MySQL 选择不到所要使用的最好索引。只保持所需的索引有利于查询优化。
如果想给已有索引的表增加索引,应该考虑所要增加的索引是否是现有多列索引的最左索引。如果是,则就不要费力去增加这个索引了,因为已经有了。
频繁更新的字段不适合创建索引频繁更新的字段不适合创建索引
7、应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
Null值不存储在索引中,因此在索引列上带is null 条件的查询不会使用索引
mysql> explain select * from user where uname is null;
+----+-------------+-------+------+---------------+------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+----------+-------------+
| 1 | SIMPLE | user | ALL | NULL | NULL | NULL | NULL | 10000000 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+----------+-------------+
同样is not null 也不走索引
可以修改成:
select id from t where num=0
8、考虑在列上进行的比较类型。
索引可用于“ <” 、 “ <= ” 、 “ = ” 、 “ >=” 、 “ > ” 和 BETWEEN 运算。
应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。优化器将无法通过索引来确定将要命中的行数,因此需要搜索该表的所有行。
mysql> explain select * from user where uname != 'haha';
+----+-------------+-------+------+---------------+------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+----------+-------------+
| 1 | SIMPLE | user | ALL | uname | NULL | NULL | NULL | 10000000 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+----------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from user where uname > 'haha';
+----+-------------+-------+-------+---------------+-------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+-------+---------+------+------+-------------+
| 1 | SIMPLE | user | range | uname | uname | 63 | NULL | 4 | Using where |
+----+-------------+-------+-------+---------------+-------+---------+------+------+-------------+
#in 和 not in 也要慎用,因为IN会使系统无法使用索引,而只能直接搜索表中的数据
select id from t where num in(1,2,3)
#对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
9、like 使用非打头字母搜索。
这也使得引擎无法利用索引。'%hello'用不上,'hello%' 用的上
mysql> explain select * from user where uname like '%haha';
+----+-------------+-------+------+---------------+------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+----------+-------------+
| 1 | SIMPLE | user | ALL | NULL | NULL | NULL | NULL | 10000000 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+----------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from user where uname like 'aha%';
+----+-------------+-------+-------+---------------+-------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+-------+---------+------+------+-------------+
| 1 | SIMPLE | user | range | uname | uname | 63 | NULL | 1 | Using where |
+----+-------------+-------+-------+---------------+-------+---------+------+------+-------------+
10、应尽量避免在 where 子句中使用 or 来连接条件,否则可能将导致引擎放弃使用索引而进行全表扫描,如:
mysql> explain select * from t1 where cat_id = 8 or cat_id = 3;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | t1 | ALL | cat_id | NULL | NULL | NULL | 31 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from t1 where cat_id = 3;
+----+-------------+-------+------+---------------+--------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+--------+---------+-------+------+-------+
| 1 | SIMPLE | t1 | ref | cat_id | cat_id | 2 | const | 14 | |
+----+-------------+-------+------+---------------+--------+---------+-------+------+-------+
11、应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描
mysql> explain select * from t1 where cat_id +1 = 3;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 31 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
12、应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描
13、如果列类型是字符串,那一定要在条件中将数据使用引号 引用起来,否则不使用索引
mysql> explain select * from user where uname = '111';
+----+-------------+-------+------+---------------+-------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+-------+---------+-------+------+-------------+
| 1 | SIMPLE | user | ref | uname | uname | 63 | const | 1 | Using where |
+----+-------------+-------+------+---------------+-------+---------+-------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from user where uname = 111;
+----+-------------+-------+------+---------------+------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+----------+-------------+
| 1 | SIMPLE | user | ALL | uname | NULL | NULL | NULL | 10000000 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+----------+-------------+
14、如果mysql估计使用全表扫描要比使用索引快,则不使用索引
当一条Query返回的数据超过了全表的 15%时,就不应该再使用索引扫描来完成这个 Query 了。对于"15%"这个数字我们并不能判定是否很准确,但是至少侧面证明了唯一性太差的字段并不适合创建索引。