MySQL中,所有数据类型的列都可以被索引,常用的存储引擎InnoDB和MyISAM能支持每个表创建16个索引。InnoDB和MyISAM使用的索引其底层算法是B-tree(B树),B-tree是一种自平衡的树,类似于平衡二叉排序树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的操作都在对数时间内完成。
接下来我们通过一个简单的例子来说明索引的意义,比如我们要根据学生的姓名来查找学生,这个场景在实际开发中应该经常遇到,就跟通过商品名称查找商品道理是一样的。我们可以使用MySQL的关键字来查看SQL的执行计划。
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: tb_student
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 11
filtered: 10.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)
在上面的SQL执行计划中,有几项值得我们关注:
select_type
:查询的类型。- SIMPLE:简单SELECT,不需要使用UNION操作或子查询。
- PRIMARY:如果查询包含子查询,最外层的SELECT被标记为PRIMARY。
- UNION:UNION操作中第二个或后面的SELECT语句。
- SUBQUERY:子查询中的第一个SELECT。
- DERIVED:派生表的SELECT子查询。
table
:查询对应的表。type
:MySQL在表中找到满足条件的行的方式,也称为访问类型,包括:ALL(全表扫描)、index(索引全扫描,只遍历索引树)、range(索引范围扫描)、ref(非唯一索引扫描)、eq_ref(唯一索引扫描)、const/system(常量级查询)、NULL(不需要访问表或索引)。在所有的访问类型中,很显然ALL是性能最差的,它代表的全表扫描是指要扫描表中的每一行才能找到匹配的行。key
:MySQL真正使用的索引,如果为NULL就表示没有使用索引。- :使用的索引的长度,在不影响查询的情况下肯定是长度越短越好。
rows
:执行查询需要扫描的行数,这是一个预估值。extra
:关于查询额外的信息。Using filesort
:MySQL无法利用索引完成排序操作。Using index
:只使用索引的信息而不需要进一步查表来获取更多的信息。Using temporary
:MySQL需要使用临时表来存储结果集,常用于分组和排序。Impossible where
:where
子句会导致没有符合条件的行。Distinct
:MySQL发现第一个匹配行后,停止为当前的行组合搜索更多的行。Using where
:查询的列未被索引覆盖,筛选条件并不是索引的前导列。
create index idx_student_name on tb_student(stuname);
再次查看刚才的SQL对应的执行计划。
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: tb_student
partitions: NULL
type: ref
possible_keys: idx_student_name
key: idx_student_name
key_len: 62
rows: 1
Extra: NULL
1 row in set, 1 warning (0.00 sec)
可以注意到,在对学生姓名创建索引后,刚才的查询已经不是全表扫描而是基于索引的查询,而且扫描的行只有唯一的一行,这显然大大的提升了查询的性能。MySQL中还允许创建前缀索引,即对索引字段的前N个字符创建索引,这样的话可以减少索引占用的空间(但节省了空间很有可能会浪费时间,时间和空间是不可调和的矛盾),如下所示。
create index idx_student_name_1 on tb_student(stuname(1));
上面的索引相当于是根据学生姓名的第一个字来创建的索引,我们再看看SQL执行计划。
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: tb_student
partitions: NULL
type: ref
possible_keys: idx_student_name
key: idx_student_name
key_len: 5
ref: const
rows: 2
filtered: 100.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)
如果要删除索引,可以使用下面的SQL。
或者
我们简单的为大家总结一下索引的设计原则:
- 最适合索引的列是出现在WHERE子句和连接子句中的列。
- 索引列的基数越大(取值多重复值少),索引的效果就越好。
- 使用前缀索引可以减少索引占用的空间,内存中可以缓存更多的索引。
- 索引不是越多越好,虽然索引加速了读操作(查询),但是写操作(增、删、改)都会变得更慢,因为数据的变化会导致索引的更新,就如同书籍章节的增删需要更新目录一样。