1 索引概述
MySQL官方对索引的定义:索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在保存数据的同时,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据, 这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
通过以下示意图理解索引:
左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址(注意:逻辑上相邻的记录在磁盘上也并不是一定物理相邻的)。在不添加索引的情况下,查询数据时只能根据逻辑地址顺序查找,时间复杂度是 O ( N ) O(N) O(N);为了加快 Col2 的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找快速获取到相应数据,时间复杂度优化为 O ( log N ) O(\text{log}N) O(logN)。
在海量数据的查询中,利用索引来查询的优势是显而易见的。一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在硬盘上。
2 索引的优点与缺点
优点
-
类似于书籍的目录索引,提高数据检索的效率,降低数据库的IO成本。
-
通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗。
缺点
-
索引也是一张表,该表中保存了主键与索引字段,并指向实体类的记录,所以索引列也是要占用空间的。
-
虽然索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行INSERT、UPDATE、DELETE 等。因为更新表时,MySQL 不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。
显然,索引以牺牲存储空间为代价换取了查询的时间效率。
3 索引结构
索引是在 MySQL 的存储引擎层中实现的,而不是在服务器层实现的。所以每种存储引擎的索引都不一定完全相同,也不是所有的存储引擎都支持所有的索引类型的。MySQL 目前支持以下 4 种索引:
- B+树索引 : 最常见的索引类型,大部分索引都支持 B+树 索引。
- 哈希索引:只有 Memory 引擎支持 , 使用场景简单 。
- R树索引(空间索引):空间索引是 MyISAM 引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少,不做特别介绍。
- Full-text 索引(全文索引) :全文索引也是 MyISAM 的一个特殊索引类型,主要用于海量文本索引,InnoDB 从 Mysql5.6 版本开始支持全文索引。
索引 | InnoDB引擎 | MyISAM引擎 | Memory引擎 |
---|---|---|---|
B+树索引 | 支持 | 支持 | 支持 |
哈希索引 | 不支持 | 不支持 | 支持 |
R树索引 | 不支持 | 支持 | 不支持 |
Full-text 索引 | 5.6版本之后支持 | 支持 | 不支持 |
通常,如果没有特别指明,数据库索引都是指 B+树(多路搜索树)索引。其中,聚集索引、复合索引、前缀索引、唯一索引等默认采用 B+树 索引,统称为索引。
MySQL 中的 B+树
MySQL 索引数据结构对经典的 B+树 进行了优化:在原 B+树 的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的 B+树,提高区间访问的性能。
4 索引分类
- 单值索引 :即一个索引只包含单个字段,一个表可以有多个单值索引
- 唯一索引 :索引字段的值必须唯一,但允许有空值
- 复合索引 :即多个字段组成一个索引
5 使用索引
索引在创建表的时候,可以同时创建, 也可以随时增加新的索引。
数据准备
CREATE TABLE `city` (
`city_id` int(11) NOT NULL AUTO_INCREMENT,
`city_name` varchar(50) NOT NULL,
`country_id` int(11) NOT NULL,
PRIMARY KEY (`city_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `country` (
`country_id` int(11) NOT NULL AUTO_INCREMENT,
`country_name` varchar(100) NOT NULL,
PRIMARY KEY (`country_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `city` (`city_id`, `city_name`, `country_id`) values(1,'西安',1);
insert into `city` (`city_id`, `city_name`, `country_id`) values(2,'NewYork',2);
insert into `city` (`city_id`, `city_name`, `country_id`) values(3,'北京',1);
insert into `city` (`city_id`, `city_name`, `country_id`) values(4,'上海',1);
insert into `country` (`country_id`, `country_name`) values(1,'China');
insert into `country` (`country_id`, `country_name`) values(2,'America');
insert into `country` (`country_id`, `country_name`) values(3,'Japan');
insert into `country` (`country_id`, `country_name`) values(4,'UK');
SELECT * FROM city;
5.1 创建索引
语法:
CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name
[USING index_type]
ON tbl_name(index_col_name,...)
为 city
表中的 city_name
字段创建索引:
CREATE INDEX idx_city_name ON city(city_name);
5.2 查看索引
语法:
SHOW INDEX FROM table_name;
查看 city
表中的索引信息:
SHOW INDEX FROM city;
可见,MySQL 中索引默认采用的数据结构就是 B+树。
5.3 删除索引
语法:
DROP INDEX index_name ON table_name;
删除 city
表上的索引 idx_city_name
:
DROP INDEX idx_city_name ON city;
5.4 ALTER命令
添加一个主键,这意味着索引值必须是唯一的,且不能为 NULL:
ALTER TABLE table_name ADD PRIMARY KEY(column_list);
创建索引的值必须是唯一的(除了 NULL 外,NULL 可能会出现多次)
ALTER TABLE table_name ADD UNIQUE index_name(column_list);
添加普通索引, 索引值可以出现多次:
ALTER TABLE table_name ADD INDEX index_name(column_list);
指定了索引为 Full-Text, 用于全文索引:
ALTER TABLE table_name ADD FULLTEXT index_name(column_list);
6 索引设计原则
索引的设计可以遵循一些已有的原则,创建索引的时候请尽量考虑符合这些原则,便于提升索引的使用效率,更高效的使用索引。
-
对查询频次较高,且数据量比较大的表建立索引。
-
索引字段的选择,最佳候选列应当从 where 子句的条件中提取,如果 where 子句中的组合比较多,那么应当挑选最常用、过滤效果最好的列的组合。
-
使用唯一索引,区分度越高,使用索引的效率越高。
-
索引可以有效的提升查询数据的效率,但索引数量不是多多益善,索引越多,维护索引的代价自然也就水涨船高。对于插入、更新、删除等 DML 操作比较频繁的表来说,索引过多,会引入相当高的维护代价,降低 DML 操作的效率,增加相应操作的时间消耗。另外索引过多的话,MySQL 也会犯选择困难病,虽然最终仍然会找到一个可用的索引,但无疑提高了选择的代价。
-
使用短索引,索引创建之后也是使用硬盘来存储的,因此提升索引访问的 I/O 效率,也可以提升总体的访问效率。假如构成索引的字段总长度比较短,那么在给定大小的存储块内可以存储更多的索引值,相应的可以有效的提升 MySQL 访问索引的 I/O 效率。
-
利用最左前缀,N 个列组合而成的组合索引,那么相当于是创建了 N个索引,如果查询时 where 子句中使用了组成该索引的前几个字段,那么这条查询 SQL 可以利用组合索引来提升查询效率。
创建复合索引:
CREATE INDEX idx_name_email_status ON tb_seller(NAME,email,STATUS);
就相当于:
- 对 name 创建索引;
- 对 name,email 创建了索引;
- 对 name,email,status 创建了索引。