目录
前言
索引是MySQL性能调优重要的一部分,所以了解索引很重要。
一、什么是索引?
索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。索引是一种数据结构。数据库索引,是数据库管理系统中的一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树以及其变种B+树。更通俗的说,索引就相当于目录。为了方便找书中的内容,通过对内容建立索引形成目录,索引是一个文件,它是要占据物理空间的。
二、索引有哪些优缺点?
1、优点:创建索引可以大大提高系统的性能。
第一, 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
第二, 可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
第三, 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
第四, 在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
第五, 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
2、缺点:
也许会有人要问:增加索引有如此多的优点,为什么不对表中的每一个列创建一个索引呢?这种想法固然有其合理性,然而也有其片面性。虽然,索引有许多优点,但是,为表中的每一个列都增加索引,是非常不明智的。这是因为,增加索引也有许多不利的一个方面。
第一, 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
第二, 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
第三, 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
三、索引使用场景(important)
上图中,根据id记录,因为id字段仅建立了主键索引,因此SQL执行可选的索引只有主键索引,如果有多个,最终会选一个较优的作为检索的依据。
#增加一个没有建立索引的字段
alter table actors add first_name varchar(16);
#按照first_name检索时可选的索引为null
explain select * from actors where first_name='李';
可以尝试在一个字段未建立索引时,根据该字段查询的效率,然后对该字段建立索引(alter table 表面 add index(字段名)),同样的SQL执行的效率,你会发现查询效率会有明显的提升(数据量越大越明显)。
order by
当我们使用order by将查询结果按照某个字段排序时,如果该字段没有建立索引,那么执行计划会将查询出的所有数据使用外部排序(将数据从硬盘分批读取到内存使用内部排序,最后合并排序结果),这个操作时很影响性能的,因为需要将查询涉及到的所有数据从硬盘中读到内存(如果单条数据大或者数据量过多都会降低效率),更无论读到内存之后的排序了。但是如果我们对该字段建立索引alter table 表名 add index(字段名),那么由于索引本身是有序的,因此直接按照索引的顺序和映射关系逐条取出数据即可。而且如果分页的,那么只用取出索引表某个范围内的索引对应的数据,而不用像上述那取出所有数据进行排序在返回某个范围内的数据。(从磁盘读取数据是最影响性能的)。
join
对join语句匹配关系(on)涉及的字段建立索引能够提高效率
索引覆盖
如果要查询的字段都建立过索引,那么引擎会直接在索引表中查询而不会访问原始数据(否则只要有一个字段没有建立索引就会做全表描),这叫索引覆盖。因此我们需要尽可能的在select后只写必要的查询字段,以增加索引覆盖的几率。
这里值得注意的是不要想着为每个字段建立索引,因为优先使用索引的优势就在于其体积小。
四、索引有哪几种类型?
主键索引:数据列不允许重复,不允许为null,一个表只能有一个主键。
唯一索引:数据列不允许重复,允许为null值,一个表允许多个列创建唯一索引。
可以通过
ALTER TABLE table_name ADD UNIQUE(column);
创建唯一索引
可以通过
ALTER TABLE table_name ADD UNIQUE(column1,column2);
创建唯一组合索引
普通索引:基本的索引类型,没有唯一的限制,允许为null值。
可以通过
ALTER TABLE table_name ADD INDEX index_name(column);
创建普通索引
可以通过
ALTER TABLE table_name ADD INDEX index_name(column1,column2,column3);
创建组合索引
全文索引:是目前搜索引擎使用的一种关键技术。
可以通过
ALTER TABLE table_name ADD FULLTEXT(column);
创建全文索引
五、索引的数据结构(B树,hash)
索引的数据结构和具体存储引擎的实现有关,在MySQL中使用较多的索引有:Hash索引,B+树索引等,而我们经常使用的InnoDB存储引擎的默认索引实现为:B+树索引。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能快;其余大部分场景,建议选择BTree索引。
B树索引mysql通过存储引擎取数据,基本上90%的人用的就是InnoDB了,按照实现方式分,InnoDB的索引类型目前只有两种:BTREE(B树)索引和HASH索引。B树索引是Mysql数据库中使用频繁的索引类型,基本所有存储引擎都支持BTree索引。通常我们说的索引不出意外指的就是(B树)索引(实际是用B+树实现的,因为在查看表索引时,mysql一律打印BTREE,所以简称为B树索引)
查询方式:
主键索引区:PI(关联保存的时数据的地址)按主键查询,
普通索引区:si(关联的id的地址,然后再到达上面的地址)。所以按主键查询,速度快
B+tree性质:
(1)n棵子tree的节点包含n个关键字,不用来保存数据而是保存数据的索引
(2)所有的叶子节点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
3.)所有的非终端结点可以看成是索引部分,结点中仅含其子树中的大(或小)关键字。
4.)B+树中,数据对象的插入和删除仅在叶节点上进行。
5.)B+树有2个头指针,一个是树的根节点,一个是小关键码的叶节点。2)哈希索引简要说下,类似于数据结构中简单实现的HASH表(散列表)一样,当我们在mysql中用哈希索引时,主要就是通过Hash算法(常见的Hash算法有直接定址法、平方取中法、折叠法、除数取余法、随机数法),将数据库字段数据转换成定长的Hash值,与这条数据的行指针一并存入Hash表的对应位置;如果发生Hash碰撞(两个不同关键字的Hash值相同),则在对应Hash键下以链表形式存储。
六、索引的基本原理
索引用来快速地寻找那些具有特定值的记录。如果没有索引,一般来说执行查询时遍历整张表。
索引的原理很简单,就是把无序的数据变成有序的查询
-
把创建了索引的列的内容进行排序
-
对排序结果生成倒排表
-
在倒排表内容上拼上数据地址链
-
在查询的时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体数据
七、索引算法有哪些?
索引算法有BTree算法和Hash算法
BTree算法
BTree是常用的mysql数据库索引算法,也是mysql默认的算法。因为它不仅可以被用在=,>,>=,<,<=和between这些比较操作符上,而且还可以用于like操作符,只要它的查询条件是一个不以通配符开头的常量,例如:
#只要它的查询条件是一个不以通配符开头的常量
select * from actors where first_name like 'jack%';
#如果一通配符开头,或者没有使用常量,则不会使用索引,例如:
select * from actors where first_name like '%jack';
Hash算法
Hash索引只能用于对等比较,例如=,<=>(相当于=)操作符号。由于是一次定位数据,不像BTree索引需要从根节点到枝节点,后才能访问到页节点这样多次IO访问,所以检索效率远高于BTree索引。
八、索引设计的原则
1、适合索引的列是出现在where子句中的列,或者连接字句中指定的列。
2、基数较小的类,索引效果较差,没有必要在此列建立索引。
3、使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间。
4、不要过度索引。索引需要额外的磁盘空间,并降低写操作的性嫩。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可。
九、创建索引的原则(important)
索引虽好,可不要贪杯,但是也不是无限制的使用,最好符合以下几个原则
(1)左前缀匹配原则,组合索引非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a=1 and b=2 and c>3 and d=4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立建立(a,b,c,d)的索引则都可以用到,a,b,d的顺序可以任意调整。
(2)较频繁作为查询条件的字段才去创建索引
(3)更新频繁字段不适合创建索引
(4)若是不能有效区分数据的列不适合做索引列(如性别,男女未知,多也就三种,区分度实在太低)
(5)尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
(6)定义有外键的数据列一定要建立索引。
(7)对于那些查询中很少涉及到的列,重复值比较多的列不要建立索引。
(8)对于定义为text、image和bit的数据类型的列不要建立索引。创建索引的三种方式,删除索引。
第一种方式:在执行create table时创建索引
create table actors(
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 命令去增加索引
alter table table_name add index index_name(column_list);
alter table 用来创建普通索引,unique索引或者primarykey主键索引。
其中table_name 是要增加索引的表名,column_list指出对哪些列进行索引,多列时各列之间用逗号分隔。
索引名index_name可自己命名,缺省时,MySQL将根据第一个索引列赋第一个索引列一个名称。另外,alter table允许在单个语句中更改多个表,因此可以在同时创建多个索引。
**第三种方式:**使用create index命令创建
create index index_name on table_name(column_list)
create index可对表增加普通索引或者unique索引。(但是,不能创建primarykey索引)
删除索引
根据索引名删除普通索引、唯一索引、全文索引:alter table 表名 drop key 索引名
alter table actorsww drop key name;
删除主键索引:alter table 表名 drop primary key(因为主键只有一个)。这里值得注意的是,如果主键自增长,那么不能直接执行此操作(自增长依赖于主键索引):
需要取消自增长再行删除:
alter table user_index
#重新定义字段
modify id int,
drop primary key
但是通常不会删除主键,因为设计主键一定于业务逻辑无关。
十、创建索引时需要注意什么?
1、非空字段:应该指定列为NOTNULL,除非你想存储NULL。在mysql中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值;
2、 取值离散大的字段:(变量各个取值之间的差异程度)的列放到联合索引的前面,可以通过count()函数查看字段的差异值,返回值越大说明字段的唯一值越多字段的离散程度高;
3、索引字段越小越好:数据库的数据存储以页为单位一页存储的数据越多一次IO操作获取的数据越大效率越高。
十一、使用索引查询一定能提高查询的性能吗?为什么?
通常,通过索引查询数据比全表扫描要快。但是我们也必须注意到它的代价。
索引需要空间来存储,也需要定期维护,每当有记录在表中增减或索引列被修改时,索引本身也会被修改。这意味着每条记录的INSERT,DELETE,UPDATE将为此多付出4,5次的磁盘I/O。因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢。使用索引查询不一定能提高查询性能,索引范围查询(INDEXRANGESCAN)适用于两种情况:
基于一个范围的检索,一般查询返回结果集小于表中记录数的30%
基于非唯一性索引的检索
十二、百万级别或以上的数据如何删除
关于索引:由于索引需要额外的维护成本,因为索引文件是单独存在的文件,所以当我们对数据的增加,修改,删除,都会产生额外的对索引文件的操作,这些操作需要消耗额外的IO,会降低增/改/删的执行效率。所以,在我们删除数据库百万级别数据的时候,查询MySQL官方手册得知删除数据的速度和创建的索引数量是成正比的。
-
所以我们想要删除百万数据的时候可以先删除索引(此时大概耗时三分多钟)
-
然后删除其中无用数据(此过程需要不到两分钟)
-
删除完成后重新创建索引(此时数据较少了)创建索引也非常快,约十分钟左右。
-
与之前的直接删除绝对是要快速很多,更别说万一删除中断,一切删除会回滚。那更是坑了。