Mysql高级学习笔记:02Mysql的索引详解

定义

MYSQL官方对索引的定义为:索引(Index)是一种帮助MYSQL高效获取数据的数据结构。一般来说,索引的本身也比较大,不可能全部存储在内存中,因此索引往往以文件的形式存储在磁盘上。

操作背景

下文中所有的操作都基于这两张表,student,class表,表结构和索引如下

mysql> desc student;
+---------+------------+------+-----+---------+----------------+
| Field   | Type       | Null | Key | Default | Extra          |
+---------+------------+------+-----+---------+----------------+
| id      | bigint     | NO   | PRI | NULL    | auto_increment |
| name    | varchar(9) | YES  |     | NULL    |                |
| age     | int        | YES  |     | NULL    |                |
| classId | bigint     | NO   |     | NULL    |                |
+---------+------------+------+-----+---------+----------------+
4 rows in set (0.02 sec)

mysql> desc class;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | bigint      | NO   | PRI | NULL    | auto_increment |
| name  | varchar(32) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.03 sec)

mysql> 

分类

普通单值索引

一个索引只包含单个列,一个表可以有多个单值索引

唯一索引

索引列的值必须唯一,但允许有空值

复合索引

一个索引有多个索引列,在使用时必须遵循最左匹配原则。

索引DDL操作

创建索引

  • CREATE INDEX 方式

语法:CREATE [UNIQUE] INDEX INDEXNAME ON TABLE_NAME(COLUMNNAME(length));

tips: 如果是CHAR/VARCHAR类型,length可以小于字段实际长度,如果是BLOB和TEXT类型,必须制定length。

mysql> create index idx_classId_age on student(classId,age);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> 
  • ALTER TABLE 方式

语法:ALTER TABLE [TABLE_NAME] ADD INDEX INDEX_NAME

mysql> alter table student add index idx_classId_age(classId,age);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> 

删除索引

  • 方式一 ALTER TABLE

语法: ALTER TABLE [TABLE_NAME] DROP INDEX [INDEX_NAME];

mysql> alter table student drop index idx_classId_age;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0
  • 方式二: DROP INDEX

语法:DROP INDEX [INDEX_NAME] ON [TABLE_NAME]

mysql> DROP INDEX IDX_CLASSID_AGE ON STUDENT;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> 

查看索引

语法: SHOW INDEX FROM [TABLE_NAME]

mysql> show index from student;
+---------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name        | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| student |          0 | PRIMARY         |            1 | id          | A         |           4 | NULL     | NULL   |      | BTREE      |         |               |
| student |          1 | idx_classId_age |            1 | classId     | A         |           3 | NULL     | NULL   |      | BTREE      |         |               |
| student |          1 | idx_classId_age |            2 | age         | A         |           4 | NULL     | NULL   | YES  | BTREE      |         |               |
+---------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.03 sec)

mysql> 

索引的优势

  • 提高了数据的检索效率,降低了数据库的IO成本
  • 降低了数据排序的成本,降低了CPU的消耗

索引的劣势

  • 索引本质上也是一种数据表,该表保存了主键与索引字段,并指向实体表的记录,需要占用一定的空间。
  • 降低了更新插入数据的效率,因为除了要保存数据意外,还需要额外的维护索引树。

什么时候需要建索引

  • 1.主键会自动建立唯一索引
  • 2.频繁作为查询条件的字段应该创建索引
  • 3.查询中需要与其他表做关联的字段应当建立索引(外键关系)
  • 4.如果可以创建复合索引的情况,优先创建复合索引
  • 5.查询中需要排序的字段应当建立索引
  • 6.查询中统计或者分组的字段应当建立索引

哪些情况不宜建立索引

  • 1.表的记录太少,没有必要建立索引
  • 2.频繁增删改的表没必要建立索引,原因见劣势(索引会降低更新表的速度),当然实际情况需要根据业务去寻找一个平衡。
  • 数据重复且分布平均的表字段,例如性别,最多也就3个值,在这种字段上建立索引意义不大,没有必要建立索引浪费空间。

索引失效的情况

在以下情况下会导致我们建立的索引失效,变成全表扫描,从而大大偏离我们的预期结果,应当避免。

  • 1.在索引上做任何操作(计算,函数,(显式或隐式)类型转换),会导致索引失效而转向全表扫描。

1.索引字段上做运算操作,引起索引失效

mysql> explain select * from student where (age) = 2;
+----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | student | NULL       | ref  | idx_age       | idx_age | 5       | const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-------+
1 row in set (0.08 sec)

mysql> explain select * from student where (age-1) = 2;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    4 |   100.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set (0.06 sec)

2.函数运算导致索引失效

mysql> explain select * from student where name  = '张三';
+----+-------------+---------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key      | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+----------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | student | NULL       | ref  | idx_name      | idx_name | 30      | const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+----------+---------+-------+------+----------+-------+
1 row in set (0.07 sec)

mysql> explain select * from student where left(name,2)  = '张三';
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    4 |   100.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set (0.07 sec)

3.类型的隐式转换导致索引失效(如字符串类型字段,查询时值得带引号)

mysql> explain select * from student where name  = 2;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ALL  | idx_name      | NULL | NULL    | NULL |    4 |    25.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set (0.07 sec)
  • 在存储索引擎中不能使用索引中范围条件右边的列(左值匹配原则)
mysql> show index from student;
+---------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name        | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| student |          0 | PRIMARY         |            1 | id          | A         |           4 | NULL     | NULL   |      | BTREE      |         |               |
| student |          1 | idx_classId_age |            1 | classId     | A         |           3 | NULL     | NULL   |      | BTREE      |         |               |
| student |          1 | idx_classId_age |            2 | age         | A         |           4 | NULL     | NULL   | YES  | BTREE      |         |               |
| student |          1 | idx_name        |            1 | name        | A         |           4 | NULL     | NULL   | YES  | BTREE      |         |               |
+---------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (0.03 sec)

mysql> explain select * from student where age = 3;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    4 |    25.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set (0.04 sec)

mysql> 

  • 尽量使用覆盖索引,减少select *

例如我们查询student表中的某些数据项,如果使用select * 则无法使用索引

mysql> explain select * from student;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
|  1 | SIMPLE      | student | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    4 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set (0.03 sec)

更改为查询某一部分(select 后面列出字段)使用覆盖索引即可以解决问题。

mysql> explain select classId,age from student;
+----+-------------+---------+------------+-------+---------------+-----------------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type  | possible_keys | key             | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+-------+---------------+-----------------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | index | NULL          | idx_classId_age | 9       | NULL |    4 |   100.00 | Using index |
+----+-------------+---------+------------+-------+---------------+-----------------+---------+------+------+----------+-------------+
1 row in set (0.02 sec)

mysql> 
  • 在查询中使用不等于(!=或<>)的时候会导致索引失效
mysql> show index from student;
+---------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name        | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| student |          0 | PRIMARY         |            1 | id          | A         |           4 | NULL     | NULL   |      | BTREE      |         |               |
| student |          1 | idx_classId_age |            1 | classId     | A         |           3 | NULL     | NULL   |      | BTREE      |         |               |
| student |          1 | idx_classId_age |            2 | age         | A         |           4 | NULL     | NULL   | YES  | BTREE      |         |               |
+---------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.03 sec)

mysql> explain select * from student where classId <> 2;
+----+-------------+---------+------------+------+-----------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys   | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+-----------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ALL  | idx_classId_age | NULL | NULL    | NULL |    4 |    75.00 | Using where |
+----+-------------+---------+------------+------+-----------------+------+---------+------+------+----------+-------------+
1 row in set (0.03 sec)

mysql> 
  • is null,is not null 也会导致索引失效
mysql> explain select * from student where classId is not null;
+----+-------------+---------+------------+------+-----------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys   | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+-----------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ALL  | idx_classId_age | NULL | NULL    | NULL |    4 |    75.00 | Using where |
+----+-------------+---------+------------+------+-----------------+------+---------+------+------+----------+-------------+
1 row in set (0.03 sec)

mysql> 
  • 尽量少用or或者in,用它来连接时会导致索引失效
mysql> explain select * from student where classId = 1 or classId = 2;
+----+-------------+---------+------------+------+-----------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys   | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+-----------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ALL  | idx_classId_age | NULL | NULL    | NULL |    4 |   100.00 | Using where |
+----+-------------+---------+------------+------+-----------------+------+---------+------+------+----------+-------------+
1 row in set (0.02 sec)

mysql> 
  • 字符串不加单引号导致索引失效(本质上做了数据类型的隐式转换运算)
mysql> show index from student;
+---------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name        | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| student |          0 | PRIMARY         |            1 | id          | A         |           4 | NULL     | NULL   |      | BTREE      |         |               |
| student |          1 | idx_classId_age |            1 | classId     | A         |           3 | NULL     | NULL   |      | BTREE      |         |               |
| student |          1 | idx_classId_age |            2 | age         | A         |           4 | NULL     | NULL   | YES  | BTREE      |         |               |
| student |          1 | idx_name        |            1 | name        | A         |           4 | NULL     | NULL   | YES  | BTREE      |         |               |
+---------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (0.03 sec)
mysql> explain select * from student where name = 2;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ALL  | idx_name      | NULL | NULL    | NULL |    4 |    25.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set (0.02 sec)

mysql> explain select * from student where name = '2';
+----+-------------+---------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key      | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+----------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | student | NULL       | ref  | idx_name      | idx_name | 30      | const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+----------+---------+-------+------+----------+-------+
1 row in set (0.01 sec)

由上面明显可以看到,当以name作为查询条件时,如果不给后面的2加上单引号,就会导致全表扫描,而加上单引号以后,就会使用索引idx_name。

  • like 以通配符开头(‘%abc…’')会导致索引失效,从而变成全表扫描
mysql> explain select * from student where name like '张%';
+----+-------------+---------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| id | select_type | table   | partitions | type  | possible_keys | key      | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+---------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | student | NULL       | range | idx_name      | idx_name | 30      | NULL |    2 |   100.00 | Using index condition |
+----+-------------+---------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
1 row in set (0.03 sec)

mysql> explain select * from student where name like '%张%';
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    4 |    25.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set (0.05 sec)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值