什么是索引?
在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。索引提供指向存储在表的指定列中的数据值的指针,然后根据您指定的排序顺序对这些指针排序。数据库使用索引以找到特定值,然后顺指针找到包含该值的行。这样可以使对应于表的SQL语句执行得更快,可快速访问数据库表中信息。当表中有大量记录时,若要对表进行查询,第一种搜索信息方式是全表搜索,是将所有记录一一取出,和查询条件进行一一对比,然后返回满足条件的记录,这样做会消耗大量数据库系统时间,并造成大量磁盘I/O操作;第二种就是在表中建立索引,然后在索引中找到符合查询条件的索引值,最后通过保存在索引中的ROWID(相当于页码)快速找到表中对应的记录。简单来说,索引就是一种数据结构,是一种排好序的快速查找的数据结构,例如给某张表的username字段建立索引,那么在查询的时候就会根据值的长度以及首字母,第二个字母,第三个字母的值来分析查找,以便于快速定位。
创建索引
主键索引
主键索引是某个字段被设置为主键以后自动建立的 语法 :
ALTER TABLE table_name add PRIMARY KEY table_name (column_name);
唯一索引
索引列的值必须唯一,但允许问null
CREATE UNIQUE INDEX index_name ON table_name (column_name);
单值索引
即索引的列只包含单列,一个表可以有多个单值索引
CREATE INDEX index_name ON table_name (column_name);
复合索引
一个索引包含多个列
CREATE INDEX index_name ON table_name (column_name1,column_name2);
查看表的索引
show index from table_name(表名)
BTree索引分析
【初始化介绍】
一颗b树,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),
如磁盘块1包含数据项17和35,包含指针P1、P2、P3,
P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。
真实的数据只存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。
非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。
【查找过程】
如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。
哪些情况下需要建立索引
1.频繁需要查询的字段,例如京东商城产品的价格区间,产品名称,型号等。
2.与其他表关联的外键字段。
3.经常用来排序的字段,也就是order by后面跟的字段。
4.经常用来统计或分组的字段,group by,max,min,avg等。
5.索引选择性接近于1的列,索引选择性=某列不重复的值 / 表记录数,例如有一个表有1000条记录,有字段为username,这个字段有990个值是不重复的,只有10个值为重复的,那这时候索引选择性就是990/1000 = 0.99,0.99是非常接近于1的,那么这种字段就适合建索引。
哪些情况下适合建立索引
1.某张表的表记录很少,这种情况下如果建立了索引,那么在进行数据更新的时候,也会对索引进行相关维护,但索引的目的就是为了提高查询效率,如果某张表的记录本来就很少,那么通常查询时效率也是快的。
2.频繁修改的字段,因为在做更新操作时,不光要更新表中的数据,还要更新维护索引的表数据。
3.where后面用不到的字段,通常where后面就是跟查询条件的,也就是如果某个字段不适合或者很少用来做查询,就不要建立索引。
4.字段值平均分布,例如gender字段的值就是两种(男或女),而且分布情况也是相对较平均的,一半一半,那这用字段就不适合建索引。
索引优化
插入以下测试数据
CREATE TABLE IF NOT EXISTS `article` (
`id` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`author_id` INT(10) UNSIGNED NOT NULL,
`category_id` INT(10) UNSIGNED NOT NULL,
`views` INT(10) UNSIGNED NOT NULL,
`comments` INT(10) UNSIGNED NOT NULL,
`title` VARBINARY(255) NOT NULL,
`content` TEXT NOT NULL
);
INSERT INTO `article`(`author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES
(1, 1, 1, 1, '1', '1'),
(2, 2, 2, 2, '2', '2'),
(1, 1, 3, 3, '3', '3');
SELECT * FROM article;
使用explain查看执行的sqlEXPLAIN SELECT id,author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1;
可以看到type为ALL,且Extra中使用到了filesort文件排序,这是损耗性能的,可以将这两个优化。
优化一:创建复合索引
create index idx_article_ccv on article(category_id,comments,views);
查看建立的索引
这样在检索时按照顺序先检索category_id字段,再检索comments,再检索views,是ccv的顺序,此时再执行刚才的sql
此时type变为了range,rows也从3降到了1,但还有filesort的问题,这里说明索引失效了,因为rows已经为1条了,但还是发生了filesort重新排序,原因是我们建好的索引是ccv的顺序,但是comments > 1打乱了之前的索引,导致索引失效,即 range 类型查询字段后面的索引无效,就产生了filesort问题。
如何优化? 通过以上分析,索引失效的原因就是,comments字段处在了views前面,那我们可以通过改变顺序或许不为comments字段添加索引。
删除索引,重新创建
create index idx_article_cvc on article(category_id,views,comments);
此次索引的顺序是cvc,comments的range放在了最后,再次执行以上sql查看
此时没有了filesort,也用上了索引 。
或者也可以直接不为comments添加索引
create index idx_article_cv on article(category_id,views);