我的理解索引其实就是一种可以加快数据访问速度的一种技术,它的本质一般就是空间换时间。
何时使用索引?
这个需要根据业务情况和实际测试来进行决断,如果你的数据量非常非常小完全没必要用索引,因为插入数据都需要去维护索引,在数据量小的情况下索引查询有可能还不如全表扫描快。
1 单列索引
ALTER TABLE table_name ADD PRIMARY KEY (column_name);
ALTER TABLE table_name ADD INDEX index_name (column_name);
CREATE UNIQUE INDEX index_name ON table(column_name);
#创建表时,创建全文索引
CREATE TABLE `t_fulltext` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`content` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#创建全文索引
ALTER TABLE `t_fulltext` ADD FULLTEXT INDEX `idx_content`(`content`);
ALTER TABLE table_name ADD INDEX index_name (column1(length));
2 组合索引
ALTER TABLE table_name ADD INDEX index_name (column1,column2);
1 hash表
Hash 是K-V存储,在查询非常的有优势,时间复杂度为O(1)。
但是存在一些瓶颈:
2 二叉树
二叉树特点:每个节点最多有2个分叉,左子树和右子树数据顺序左小右大。时间复杂度最好的情况下为O(logN)
但是对数据的分布要求严格,在某些情况下二叉树可能会退化为单链表,那其实也就是相当于全表扫描。
3 红黑树
其实在红黑树之前应该还有一个平衡二叉树,红黑树的本质也是平衡二叉树,只不过通过红黑染色的方法极大增强了性能。红黑树能够以O(log2 n)的时间复杂度进行搜索、插入、删除操作。
它也存在一定瓶颈:
4 B 树
多叉树降低了树的高度,减少了磁盘IO
1) B树的节点中存储着多个元素,每个节点内有多个分叉。
2) 节点中的元素包含键值和数据,节点中的键值从大到小排列。也就是说,在所有的节点都储存数据。
3) 父节点当中的元素不会出现在子节点中。
4) 所有的叶子结点都位于同一层,叶节点具有相同的深度,叶节点之间没有指针连接。
优点:
磁盘IO次数会大大减少。
比较是在内存中进行的,比较的耗时可以忽略不计。
B树的高度相比于平衡二叉树会大幅缩小,所以使用B树构建索引可以很好的提升查询的效率。
缺点:
B树不支持范围查询的快速查找:如果我们想要查找15和26之间的数据,查找到15之后,需要回到根节点重新遍历查找,需要从根节点进行多次遍历,查询效率有待提高。
空间占用较大:如果data存储的是行记录,行的大小随着列数的增多,所占空间会变大。一个页中可存储的数据量就会变少,树相应就会变高,磁盘IO次数就会变大。
5 B+树
主角终于登场了,Mysql就是选用的这种结构。
B+树:只有叶子节点才会存储数据,非叶子节点只存储键值。叶子节点之间使用双向指针连接,最
底层的叶子节点形成了一个双向有序链表(从而支持了范围查询)。
等值查询:假如我们查询值等于15的数据。查询路径磁盘块1->磁盘块2->磁盘块5。
范围查询:假如我们想要查找15和26之间的数据。
1 主键索引(非必须,但是在InnoDB中是必须的)
表t_user_myisam的索引存储在索引文件t_user_myisam.MYI中,数据文件存储在数据文件t_user_myisam.MYD中。
2 辅助索引
在 MyISAM 中,辅助索引和主键索引的结构是一样的,没有任何区别,叶子节点的数据存储的都是行记录的磁盘地址。只是主键索引的键值是唯一的,而辅助索引的键值可以重复。
后面的举例都是基于这个这个表
CREATE TABLE `t_user_innodb` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_age` (`age`) USING BTREE
) ENGINE=InnoDB;
insert into t_user_innodb values(15,'Nick',5);
insert into t_user_innodb values(18,'Zero',22);
insert into t_user_innodb values(20,'Tom',34);
insert into t_user_innodb values(30,'Nick',55);
insert into t_user_innodb values(49,'Mary',22);
insert into t_user_innodb values(50,'James',77);
insert into t_user_innodb values(56,'John',89);
insert into t_user_innodb values(77,'Lily',100);
1 主键索引(InnoDB中为又为聚簇索引,因为索引和数据放到一起)
主键索引的叶子节点会存储数据行,辅助索引只会存储主键值。
nnoDB创建索引的具体规则如下:
select * from t_user_innodb where id=30;
IO 总次数为3次
2)范围查询
select * from t_user_innodb where id between 30 and 49;
可以看到,因为在主键索引中直接存储了行数据,所以InnoDB在使用主键查询时可以快速获取行数据。
当表很大时,与在索引树中存储磁盘地址的方式相比,因为不用再去磁盘中获取数据,所以聚簇索引通常可以节省磁盘IO操作。
2 辅助索引
除聚簇索引之外的所有索引都称为辅助索引,InnoDB的辅助索引只会存储主键值而非磁盘地址。
使用辅助索引需要检索两遍索引:
select * from t_user_innodb where age=22;
2)什么是回表查询?
根据在辅助索引树中获取的主键id,到主键索引树检索数据的过程称为回表查询。
3)范围查询
select * from t_user_innodb where age between 30 and 49;
3 组合索引
表t_multiple_index,id为主键列,创建了一个联合索引idx_abc(a,b,c),构建的B+树索引结构如图所示。索引树中节点中的索引项按照(a,b,c)的顺序从大到小排列,先按照a列排序,a列相同时按照b列排序,b列相同按照c列排序。在最底层的叶子节点中,如果两个索引项的a,b,c三列都相同,索引项按照主键id排序类似于基数排序
。
CREATE TABLE `t_multiple_index` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
`c` varchar(10) DEFAULT NULL,
`d` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_abc` (`a`,`b`,`c`)
) ENGINE=InnoDB;
insert into t_multiple_index (a,b,c,id,d) values(1 ,1 ,4,5,'dll');
insert into t_multiple_index (a,b,c,id,d) values(1 ,5 ,4,2,'doc');
insert into t_multiple_index (a,b,c,id,d) values(5 ,3 ,6,7,'img');
insert into t_multiple_index (a,b,c,id,d) values(13,14,3,4,'xml');
insert into t_multiple_index (a,b,c,id,d) values(13,16,4,1,'txt');
insert into t_multiple_index (a,b,c,id,d) values(13,16,5,3,'pdf');
insert into t_multiple_index (a,b,c,id,d) values(13,16,5,6,'exe');
insert into t_multiple_index (a,b,c,id,d) values(14,14,14,8,'ddd');
select * from t_multiple_index where a=13 and b=16 and c=4;
如果我们有一些字段频繁的要放在一起进行查询组合索引是一个非常好的选择,比起单个索引来说降低了存储空间,时间上也比分别查询多个辅助索引要快。同时频繁出现在order by和group by语句中的列,建议按照顺序去创建组合索引(因为存在最左前缀匹配)。
select * from t_multiple_index where a=13;
select * from t_multiple_index where a=13 and b=16;
select * from t_multiple_index where a=13 and b=16 and c=4;
select * from t_multiple_index where a=13 and b>13;
select * from t_multiple_index where a>11 and b=14;
select * from t_multiple_index where a=16 and c=4;
而如果查询条件不包含a列,比如筛选条件只有(b,c)或者c列是无法使用组合索引的。下面的查询没有用到索引。
select * from t_multiple_index where b=16 and c=4;
select * from t_multiple_index where c=4;
4 覆盖索引
覆盖索引主要是为了解决回表的问题,如果我们查的辅助措意或者组合索引已经包含了全部我们要投影的列数据,这个时候其实是没有必要进行回表查询索引的,这个是不需要我们建立的,MySql会做相应的工作。
#这条SQL语句就会走覆盖索引不用回表查询
select a,b from t_multiple_index where a=13 and b=16;
select a,b,c,id from t_multiple_index where a=13 and b=16;
这个其实是一种优化,Mysql会充分利用Where中的条件,减少减少存储引擎必须访问基表的次数以及MySQL服务器必须访问存储引擎的次数。
可用于 InnoDB 和 MyISAM 表,对于InnoDB表ICP仅用于辅助索引
#关闭ICP
SET optimizer_switch = 'index_condition_pushdown=off';
#开启ICP
SET optimizer_switch = 'index_condition_pushdown=on';
select * from t_multiple_index where a=13 and b>15 and c='5' and d='pdf';
insert into t_multiple_index (a,b,c,id,d) values(1 ,1 ,4,5,'dll');
insert into t_multiple_index (a,b,c,id,d) values(1 ,5 ,4,2,'doc');
insert into t_multiple_index (a,b,c,id,d) values(5 ,3 ,6,7,'img');
insert into t_multiple_index (a,b,c,id,d) values(13,14,3,4,'xml');
insert into t_multiple_index (a,b,c,id,d) values(13,16,4,1,'txt');
insert into t_multiple_index (a,b,c,id,d) values(13,16,5,3,'pdf');
insert into t_multiple_index (a,b,c,id,d) values(13,16,5,6,'exe');
insert into t_multiple_index (a,b,c,id,d) values(14,14,14,8,'ddd');
insert into t_multiple_index (a,b,c,id,d) values(14,14,14,9,'ddd');
insert into t_multiple_index (a,b,c,id,d) values(14,14,14,10,'ddd');