mySql索引详解
mySql中索引类型
普通索引(
key
),唯一索引(unique key
),主键索引(primary key
),全文索引(fulltext key
)
三种索引的索引方式是一样的,只不过对索引的关键字有不同的限制:
- 普通索引:对关键字没有限制
- 唯一索引:要求记录提供的关键字不能重复
- 主键索引:要求关键字唯一且不为null
主键索引和普通索引的查询有什么区别?
如果语句是select * from T where ID=500,即主键查询方式,则只需要搜索ID这棵B+树;
如果语句是select * from T where k=5,即普通索引查询方式,则需要先搜索k索引树,得到ID的值为500,再到ID索引树搜索一次。这个过程称为回表。
也就是说,基于非主键索引的查询需要多扫描一棵索引树。因此,我们在应用中应该尽量使用主键查询。
回到主键索引树搜索的过程,我们称为回表
主键长度越小
,普通索引的叶子节点就越小
,普通索引占用的空间
也就越小
。
Mysql索引管理语法
查看索引:
show create table `table_name` ---查看表创建结构
desc 表名
表创建后添加索引
create TABLE user_index(
id int auto_increment primary key,
first_name varchar(16),
last_name VARCHAR(16),
id_card VARCHAR(18),
information text
);
-- 更改表结构
alter table user_index
-- 创建一个first_name和last_name的复合索引,并命名为name
add key name (first_name,last_name),
-- 创建一个id_card的唯一索引,默认以字段名作为索引名
add UNIQUE KEY (id_card),
-- 鸡肋,全文索引不支持中文
add FULLTEXT KEY (information);
1.添加PRIMARY KEY(主键索引)
ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` )
2.添加UNIQUE(唯一索引)
ALTER TABLE `table_name` ADD UNIQUE ( `column` )
3.添加INDEX(普通索引)
ALTER TABLE `table_name` ADD INDEX index_name ( `column` )
4.添加FULLTEXT(全文索引)
ALTER TABLE `table_name` ADD FULLTEXT ( `column`)
5.添加多列索引
ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3` )
创建表时指定索引
CREATE TABLE user_index2 (
id INT auto_increment PRIMARY KEY,
first_name VARCHAR (16),
last_name VARCHAR (16),
id_card VARCHAR (18),
information text,
KEY name (first_name, last_name),
FULLTEXT KEY (information),
UNIQUE KEY (id_card)
);
删除索引
根据索引名删除普通索引、唯一索引、全文索引:alter table 表名 drop KEY 索引名
alter table user_index drop KEY name;
alter table user_index drop KEY id_card;
alter table user_index drop KEY information;
删除主键索引:
alter table user_index
-- 重新定义字段(ps: 如果主键索引是自增,需要取消自增长再行删除)
MODIFY id int,
drop PRIMARY KEY
重建索引:
重建索引能将索引中的空洞去除、让索引更紧缩、占用空间变小。当索引文件过大的时候可以试试重建索引
alter table T add index(k); -- 重建普通索引:
alter table T add primary key(id); -- 重建主键索引:
mysql索引类型(按存储结构划分)
说明:索引是存储引擎层次的东西、不同的存储引擎支持的索引类型也不同。
BTree索引
1、MySQL的BTree索引使用的是B树中的B+Tree
,是大多数 MySQL 存储引擎的默认索引类型
ps:但对于主要的两种存储引擎(MyISAM和InnoDB)的实现方式是不同的。
2、因为不再需要进行全表扫描,只需要对树进行搜索即可,所以查找速度快很多。
3、除了用于查找,还可以用于排序
和分组
。
4、可以指定多个列作为索引列
,多个索引列共同组成键。
5、适用于全键值、键值范围和键前缀查找,其中键前缀查找只适用于最左前缀查找。如果不是按照索引列的顺序进行查找,则无法使用索引
BTree和红黑树比较
红黑树等平衡树也可以用来实现索引,但是文件系统及数据库系统普遍采用 B+ Tree 作为索引结构,主要有以下两个原因
(一)更少的查找次数
平衡树查找操作的时间复杂度和树高 h 相关,O(h)=O(logdN),其中 d 为每个节点的出度。
红黑树的出度为 2,而 B+ Tree 的出度一般都非常大,所以红黑树的树高 h 很明显比 B+ Tree 大非常多,查找的次数也就更多。
(二)利用磁盘预读特性
为了减少磁盘 I/O 操作,磁盘往往不是严格按需读取,而是每次都会预读
。预读过程中,磁盘进行顺序读取,顺序读取不需要进行磁盘寻道,并且只需要很短的旋转时间,速度会非常快。
操作系统一般将内存和磁盘分割成固定大小的块,每一块称为一页,内存与磁盘以页为单位交换数据。数据库系统将索引的一个节点的大小设置为页的大小,使得一次 I/O 就能完全载入一个节点。并且可以利用预读特性,相邻的节点也能够被预先载入。
BTree索引在(MyISAM和InnoDB)的不同实现方式
MyISAM实现
B+Tree叶节点的data域存放的是数据记录的地址
。在索引检索的时候,首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其 data(叶子节点) 域的值,然后以 data 域的值为地址读取相应的数据记录。这被称为非聚簇索引
。
InnoDB实现
其数据文件本身就是索引文件
。相比MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按B+Tree组织的一个索引结构,树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。这被称聚簇索引(或聚集索引)
。而其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不同的地方。在根据主索引搜索时,直接找到key所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,再走一遍主索引。 因此,在设计表的时候,不建议使用过长的字段作为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。
哈希索引
哈希索引能以 O(1) 时间
进行查找,但是失去了有序性:
无法用于排序与分组;
只支持精确查找,无法用于部分查找和范围查找。
InnoDB 存储引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的非常频繁时,会在 B+Tree 索引之上再创建一个哈希索引,这样就让 B+Tree 索引具有哈希索引的一些优点,比如快速的哈希查找。
在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快。其余大部分场景,建议选择BTree索引。
全文索引
MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而不是直接比较是否相等。
查找条件使用 MATCH AGAINST,而不是普通的 WHERE。
全文索引使用倒排索引实现
,它记录着关键词到其所在文档的映射。
ps:InnoDB 存储引擎在 MySQL 5.6.4 版本中也开始支持全文索引。
空间数据索引
MyISAM 存储引擎支持空间数据索引(R-Tree),可以用于地理数据存储。空间数据索引会从所有维度来索引数据,可以有效地使用任意维度来进行组合查询。
必须使用 GIS 相关的函数来维护数据。
分析索引使用情况 explain
可以通过explain selelct来分析SQL语句执行前的执行计划:
什么样的列段适合建立索引
where 条件后面使用:
给where条件中的字段建立索引能提高新能
order by:
给参与order by 的字段建立索引提高性能,因为 建立索引后此字段就有序了。能 极大提高新能。如果不建立索引那么全部数据都要使用外部排序效率非常低
join on
:对join语句匹配关系(on)涉及的字段建立索引能够提高效率
索引覆盖
:当select 查询的字段 包含在索引中那么就不需要回表。比如给 name,age 这个两个列段建立索引 然后 select name,age from user where name = ‘zs’.就能利用到覆盖索引。
or条件
:一但有一边无索引可用就会导致整个SQL语句的全表扫描
注意:索引不是越多越好,索引是能提高查询新能,但是会降低增删改的新能。并且会占用跟多的磁盘空间。
语法细节(要点)
在满足索引使用的场景下(
where
/order by
/join on
或索引覆盖),索引也不一定被使用
字段要独立出现
比如下面两条SQL语句在语义上相同,但是第一条会使用主键索引而第二条、第三条不会使用到索引。
select * from user where id = 20-1;
select * from user where id+1 = 20;
select * from user where xxx(id) = 20; --- xxx是一个函数
多列索引
在需要使用多个列作为条件进行查询
时,使用多列索引比使用多个单列索引性能更好。例如下面的语句中,最好把 actor_id 和 film_id 设置为多列索引。
SELECT film_id, actor_ id FROM sakila.film_actor WHERE
actor_id = 1 AND film_id = 1;
like查询,不能以通配符开头
title字段建立索引,但是当我们like查不注意的时候会导致索引失效
select * from article where title like '%mysql%'; --无法使用索引全表扫描
select * from article where title like 'mysql%'; -- 能使用到索引
索引列的顺序
让选择性最强的索引列放在前面。
索引的选择性是指:不重复的索引值和记录总数的比值
。最大值为 1,此时每个记录都有唯一的索引与其对应。选择性越高,查询效率也越高。
例如下面显示的结果中 customer_id 的选择性比 staff_id 更高,因此最好把 customer_id 列放在多列索引的前面。
SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity,
COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity,
COUNT(*)
FROM payment;
staff_id_selectivity: 0.0001
customer_id_selectivity: 0.0373
COUNT(*): 16049
前缀索引
语法:index(field(10))
,使用字段值的前10个字符建立索引,默认是使用字段的全部内容建立索引。前缀索引能节省空间,但会增加查询扫描次数,并且不能使用覆盖索引;
区分度问题:前缀的区分度要高。比如密码就适合建立前缀索引,因为密码几乎各不相同。
我们可以利用select count(*)/count(distinct left(password,prefixLen));
,通过从调整prefixLen
的值(从1自增)查看不同前缀长度的一个平均匹配度,接近1时就可以了(表示一个密码的前prefixLen
个字符几乎能确定唯一一条记录)
**倒序存储,再创建前缀索引:**用于绕过字符串本身前缀的区分度不够的问题,比如为我们要给身份证字段建立索引,但是身份证前面几位都差不多。我们主要是根据后面的几位来做区分。这时候就可以使用 倒序存储,再创建前缀索引
覆盖索引
由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。
索引的复用能力。因为可以支持最左前缀
,所以当已经有了(a,b)
这个联合索引后,一般就不需要单独在a
上建立索引了。因此,第一原则是,如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的。
最左前缀原则
MySQL中的索引可以以一定顺序引用多列,这种索引叫作联合索引。如User表的name和city加联合索引就是(name,city)o而最左前缀原则指的是,如果查询的时候查询条件精确匹配索引的左边连续一列或几列,则此列就可以被用到。如下:
select * from user where name=xx and city=xx ; //可以命中索引
select * from user where name=xx ; // 可以命中索引
select * from user where city=xx; // 无法命中索引
这里需要注意的是,查询的时候如果两个条件都用上了,但是顺序不同,如 city= xx and name =xx
,那么现在的查询引擎会自动优化为匹配联合索引的顺序,这样是能够命中索引的.
列段重复少的放前面,多的放后面。ORDERBY子句也遵循此规
注意避免冗余索引
冗余索引指的是索引的功能相同,能够命中 就肯定能命中 ,那么 就是冗余索引如(name,city )和(name )这两个索引就是冗余索引,能够命中后者的查询肯定是能够命中前者的 在大多数情况下,都应该尽量扩展已有的索引而不是创建新索引。
MySQLS.7 版本后,可以通过查询 sys 库的 schemal_r dundant_indexes
表来查看冗余索引
索引下推 (5.6版本开始后)
以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
导致索引失效的场景
1.建什么索引用什么索引,顺序也最好保持一致
2.最佳左前缀索引名称命名(如字段name,age,city,则索引命名应该是nameAgeCity或者xxx_nameAgeCity,顺序很重要)
3.不在索引列上做任何操作(计算,函数,or,类型转换),会导致索引失效而转向全表扫描
4.存储引擎不能使用索引中范围条件右边的列(如name=‘lin’ and age>25 and city=‘qingdao’,则age后面的索引会实效)
5.尽量使用覆盖索引(只访问索引的查询(索引列和要查询的列一致)),减少select *
6.MySQL在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
7. is null,is not null 也无法使用索引
8.like以通配符在这(’%abc’,’%abc%’)两种情况会索引实效变成全表扫描,‘abc%‘则不会,若要’%abc’,’%abc%'不失效,建议使用覆盖索引,且查询的字段要少于索引或者与索引一致,不使用select *。如为name,age,city建了索引,请这么使用:select name或者select age,或者select city或者select name,age,city。如果select name,age,city,email则会全表扫描
9.字符串不加引号索引失效,
10.少用or,用他来连接时索引会失效
11.select * from A where exists (select 1 from where b.id=A.id)#当A表的数据系小于B表时,用exists优于in
12.使用join代替子查询
索引的优点
1、大大减少了服务器需要扫描的数据行数。
2、帮助服务器避免进行排序和分组,以及避免创建临时表(B+Tree 索引是有序的,可以用于 ORDER BY 和 GROUP BY 操作。临时表主要是在排序和分组过程中创建,因为不需要排序和分组,也就不需要创建临时表)。
3、将随机 I/O 变为顺序 I/O(B+Tree 索引是有序的,会将相邻的数据都存储在一起)。
索引的使用条件
1、对于非常小的表、大部分情况下简单的全表扫描比建立索引更高效;
2、对于中到大型的表,索引就非常有效;
3、但是对于特大型的表,建立和维护索引的代价将会随之增长。这种情况下,需要用到一种技术可以直接区分出需要查询的一组数据,而不是一条记录一条记录地匹配,例如可以使用分区技术。