♣ \color{red}{\clubsuit} ♣
索引是面试中较常考的考点之一。
比如面试官会问你,索引为啥能提高查询速度?如果不知道,那就往下看吧~
索引相当于一本书的目录,通过目录我们可以迅速定位书中要找的内容。MySQL中的索引也是一样,它是一种帮助MySQL高效获取数据的数据结构(树)
索引怎么实现的?为什么用B+树?
索引是数据库中一个用于排序的数据结构,用来快速查询数据库中的数据。Mysql数据库使用B+树来实现索引的。B+树的特点就是叶子节点包含了所有的关键字信息和data数据,非叶子节点只包含子节点的最大或者最小关键字,用来实现索引。
好处:既能实现快速查找,相比于B树又节约了内存空间。
Mysql数据库中的索引实现主要依赖于两个存储引擎,MyISAM和InnoDB,都是使用B+树作为索引结构。区别就是MyISAM中使用B+树的叶子节点的data域存放数据的内存地址,而InnoDB中树的叶节点data域保存了完整的数据。
建索引的优缺点
优点:大大加快对数据的查询速度
缺点:占物理空间,对数据库进行增删改的时候也要动态的维护索引。
♢ \color{green}{\diamondsuit} ♢提高查询速度
♢ \color{blue}{\diamondsuit} ♢确保数据的唯一性
♢ \color{pink}{\diamondsuit} ♢可以加速表和表之间的连接,实现表和表之间的参照完整性
♢ \color{red}{\diamondsuit} ♢使用分组和排序子句进行数据检索时,可以减少分组和排序的时间
♢ \color{grey}{\diamondsuit} ♢全文检索字段进行搜索优化
索引如何提高查询速度呢
假如我们有如下的一张表,在没有引入索引之前,假如我们想查询表中的一条年龄为20的数据,那么几乎要遍历完整个表才能查询到这条数据;现在我们引入索引,并且以age列作为索引列来构建出一个二叉树的索引结构来,具体的构造过程是先把表中第一行的age(49)作为二叉树的头结点,然后顺序往下遍历,把小于头节点的age放到左边,大于头结点的age放到右边,表中数据遍历完以后就构造出了右图所示的二叉树索引结构。现在我们再来试着查找年龄为20的数据所需要的过程:
1.将age为20的数据先和头结点的49比较(20<49),所以往左边走
2.来到树形结构的第二层,继续和21比较(20<21),继续往左边走
3.来到树形结构的第三层,和20比较(20=20)查,找成功
加上索引以后只需三次就查找成功了,可见索引确实可以提高我们SQL的查询效率。
目前大部分数据库系统及文件系统都采用B-Tree或其变种B+Tree作为索引结构,它的具体实现就在我们上面提到的引擎层的存储引擎中。像上面提到的MyISAM存储引擎就使用B-Tree来实现主键索引、唯一索引和非主键索引等。而InnoDB中的非主键索引使用的是B-Tree数据结构,主键索引则使用的是B+Tree。
那么B-Tree和B+Tree到底有什么区别呢?
B-Tree:它类似于像上面构建的普通二叉树,但是B-树允许每个节点有更多的子节点(二叉树只允许有两个),B-树示意图如下:
B-树的特点:
(1)所有键值分布在整个树中(B+Tree只分布在叶子节点中)
(2)任何关键字出现且只出现在一个节点中
(3)搜索有可能在非叶子节点结束
(4)在关键字全集内做一次查找,性能逼近二分查找算法
B+Tree:B+树是B-树的变体,B+树的示意图为: 从图中也可以看到,B+树与B树的不同在于:
(1)所有关键字存储在叶子节点,非叶子节点不存储真正的data
(2)为所有叶子节点增加了一个链指针
B+树相对于B树在查询上会更加优胜。
因为
1、表的主键、外键必须有索引;外键是唯一的,而且经常会用来查询
2、数据量超过300的表应该有索引;
3、经常与其他表进行连接的表,在连接字段上应该建立索引;经常连接查询,需要有索引
4、经常出现在Where子句中的字段,加快判断速度,特别是大表的字段,应该建立索引,建立索引,一般用在select ……where f1 and f2 ,我们在f1或者f2上建立索引是没用的。只有两个使用联合索引才能有用
5、经常用到排序的列上,因为索引已经排序。
6、经常用在范围内搜索的列上创建索引,因为索引已经排序了,其指定的范围是连续的
简单记就是唯一、不为空、经常被查询的字段
主键:
某一个属性组能唯一标识一条记录
如:学生表(学号,姓名,班级,性别等等),学号时唯一标识的,可以作为主键。
特点:
最常见的索引类型
确保数据记录的唯一性
确定特定数据记录在数据库中的位置
实例:
CREATE TABLE `表名`(、
`GradeID` INT(11) AUTO_INCREMENT PRIMARY KEY,
#或 PRIMARY KEY(`GradeID`)
)
作用:
避免同一个表中某数据列中的值重复
与主键索引的区别
主键索引只能有一个
唯一索引可有多个
实例:
CREATE TABLE `Grade`(
`GradeID` INT(11) AUTO_INCREMENT PRIMARY KEY,
`GradeName` VARCHAR(32) NOT NULL UNIQUE
#或 UNIQUE KEY ` GradeID`(`GradeID`)
作用:
快速定位特定数据
注意:
index 和 key 关键字都可以设置常规索引
应加在查询条件的字段
不易添加太多常规索引,影响数据的插入,删除和修改操作
实例:
##创建表时添加
CREATE TABLE `result`{
//省略一些代码
INDEX / KEY `ind` (`studentNo`,`subjectNo`)
}
##创建后追加
ALTER TABLE `result` ADD INDEX `ind` (`studentNo`,`subjectNo`);
作用:
快速定位特定数据
注意:
只能用于MyISAM类型的数据表
只能用于CHAR ,VARCHAR,TEXT数据列类型
使用大型数据集
实例:
CREATE TABLE `student`(
#省略一些sql语句
FULLTEXT(`StudentName`)
)ENDINE=MYISAM;
ALTER TABLE employee ADD FULLTEXT(`first_name`)
唯一索引补充
它与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。它有以下几种创建方式:
创建索引
CREATE UNIQUE INDEX indexName ON mytable(username(length))
修改表结构
ALTER table mytable ADD UNIQUE [indexName] (username(length))
创建表的时候直接指定
CREATE TABLE mytable(
ID INT NOT NULL,
username VARCHAR(16) NOT NULL,
UNIQUE [indexName] (username(length))
);
基本索引
这是最基本的索引,它没有任何限制。它有以下几种创建方式:
CREATE INDEX indexName ON mytable(username(length));
如果是CHAR,VARCHAR类型,length可以小于字段实际长度;如果是BLOB和TEXT类型,必须指定 length。
修改表结构(添加索引)
ALTER table tableName ADD INDEX indexName(columnName)
创建表的时候直接指定
CREATE TABLE mytable(
ID INT NOT NULL,
username VARCHAR(16) NOT NULL,
INDEX [indexName] (username(length))
);
删除索引的语法
DROP INDEX [indexName] ON mytable;
组合索引
为了形象地对比单列索引和组合索引,为表添加多个字段:
CREATE TABLE mytable(
ID INT NOT NULL,
username VARCHAR(16) NOT NULL,
city VARCHAR(50) NOT NULL,
age INT NOT NULL
);
为了进一步榨取MySQL的效率,就要考虑建立组合索引。就是将 name, city, age建到一个索引里:
ALTER TABLE mytable ADD INDEX name_city_age (name(10),city,age);
建表时,usernname长度为 16,这里用 10。这是因为一般情况下名字的长度不会超过10,这样会加速索引查询速度,还会减少索引文件的大小,提高INSERT的更新速度。
如果分别在 usernname,city,age上建立单列索引,让该表有3个单列索引,查询时和上述的组合索引效率也会大不一样,远远低于我们的组合索引。虽然此时有了三个索引,但MySQL只能用到其中的那个它认为似乎是最有效率的单列索引。
建立这样的组合索引,其实是相当于分别建立了下面三组组合索引
usernname,city,age usernname,city usernname 为什么没有 city,age这样的组合索引呢?这是因为MySQL组合索引“最左前缀”的结果。简单的理解就是只从最左面的开始组合。并不是只要包含这三列的查询都会用到该组合索引
下面的几个SQL就会用到这个组合索引:
SELECT *
FROM mytable
WHREE username="admin" AND city="株洲";
SELECT * FROM mytable WHREE username="admin"
而下面几个则不会用到
SELECT * FROM mytable WHREE age=20 AND city="株洲"
SELECT * FROM mytable WHREE city="株洲"
一般来说,在WHERE和JOIN中出现的列需要建立索引,但也不完全如此, 因为MySQL只对<,<=,=,>,>=,BETWEEN,IN,以及某些时候的LIKE才会使用索引。
例如:
SELECT t.Name
FROM mytable t LEFT JOIN mytable m
ON t.Name=m.username
WHERE m.age=20 AND m.city=’株洲‘
此时就需要对city和age建立索引,由于mytable表的userame也出现在了JOIN子句中,也有对它建立索引的必要。
刚才提到只有某些时候的LIKE才需建立索引。因为在以通配符%和_开头作查询时,MySQL不会使用索引。
例如下句会使用索引:
SELECT * FROM mytable WHERE username like’admin%’;
而下句就不会使用:
SELECT * FROM mytable WHEREt Name like‘%admin’;
因此,在使用LIKE时应注意以上的区别。
上面都在说使用索引的好处,但过多的使用索引将会造成滥用。因此索引也会有它的缺点:
虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。
建立索引会占用磁盘空间的索引文件。一般情况这个问题不太严重,但如果你在一个大表上创建了多种组合索引,索引文件会膨胀很快。
索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询语句。
【须知】
使用索引时,有以下一些技巧和注意事项:
select id from userinfo where YEAR(adddate)<2007;
//将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成
select id from userinfo where adddate<‘2007-01-01’;
select id from t where num in(1,2,3)
//对于连续的数值,能用 between 就不要用 in 了
select id from t where num between 1 and 3
select id from t where num=10 or num=20;
可以这样查询:
select id from t where num=10
union all
select id from t where num=20
上面提到数据库引擎,如果不清楚的童鞋可以看看这篇☞MySQL两种存储引擎
推荐阅读☞B-Tree和B+树详解
本篇参考90%的程序员不懂数据库的索引知识,看完之后你就懂了
mysql索引的新手入门详解