数据库索引的原理、分类、优缺点、为什么不用B树、红黑树、Hash?什么时候该用索引?索引什么时候会失效?

1. 什么是索引

数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询,更新数据库中表的数据.

索引的实现通常使用B树和变种的B+树(mysql常用的索引就是B+树)

索引是在存储引擎中实现的,也就是说不同的存储引擎,会使用不同的索引。

MyISAM和InnoDB存储引擎:只支持BTREE索引,也就是说默认使用BTREE,不能够更换。

MEMORY/HEAP存储引擎:支持HASH和BTREE索引

2. 索引的种类

MySQL的索引有普通索引,唯一索引,主键索引、组合索引、全文索引。

  • 普通索引:MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了查询数据更快一 点。
  • 唯一索引:索引列中的值必须是唯一的,但是允许为空值。
  • 主键索引:是一种特殊的唯一索引,不允许有空值。(主键约束,就是一个主键索引)
  • 组合索引:在表中的多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用,使用组合索引时遵循最左前缀集合。例如,这里由id、name和age3个字段构成的索引,索引行中就按id/name/age的顺序存放,索引可以索引下面字段组合(id,name,age)、(id,name)或者(id)。如果要查询的字段不构成索引最左面的前缀,那么就不会是用索引,比如,age或者(name,age)组合就不会使用索引查询。
  • 全文索引:全文索引,只有在MyISAM引擎上才能使用,只能在CHAR,VARCHAR,TEXT类型字段上使用全文索引,介绍了要求,说说什么是全文索引,就是在一堆文字中,通过其中的某个关键字等,就能找到该字段所属的记录行。一般开发中,不贵用到全文索引,因为其占用很大的物理空间和降低了记录修改性,故较为少用。 

为什么要使用联合索引

  • 减少开销。建一个联合索引(col1,col2,col3),实际相当于建了(col1),(col1,col2),(col1,col2,col3)三个索引。每多一个索引,都会增加写操作的开销和磁盘空间的开销。对于大量数据的表,使用联合索引会大大的减少开销!
  • 覆盖索引。对联合索引(col1,col2,col3),如果有如下的sql: select col1,col2,col3 from test where col1=1 and col2=2。那么MySQL可以直接通过遍历索引取得数据,而无需回表,这减少了很多的随机io操作。减少io操作,特别的随机io其实是dba主要的优化策略。所以,在真正的实际应用中,覆盖索引是主要的提升性能的优化手段之一。
  • 效率高。索引列越多,通过索引筛选出的数据越少。有1000W条数据的表,有如下sql:select from table where col1=1 and col2=2 and col3=3,假设假设每个条件可以筛选出10%的数据,如果只有单值索引,那么通过该索引能筛选出1000W10%=100w条数据,然后再回表从100w条数据中找到符合col2=2 and col3= 3的数据,然后再排序,再分页;如果是联合索引,通过索引筛选出1000w10% 10% *10%=1w,效率提升可想而知!

这里提一下最左前缀原则 

联合索引(col1,col2,col3),实际相当于建了(col1),(col1,col2),(col1,col2,col3)三个索引。

比如给a,b,c加上索引,那么a,b可以用到索引,a,c也可以用到索引,但b,c是用不到的。

包括这个a必须要在用到的第一个索引处。

本节参考文献:铁柱同学的回答  和这个大佬的回答

 

3. 索引如何创建

# 方法一:创建表时
       CREATE TABLE 表名 (
                 字段名1  数据类型 [完整性约束条件…],
                 字段名2  数据类型 [完整性约束条件…],
                 [UNIQUE | FULLTEXT | SPATIAL ]   INDEX | KEY
                 [索引名]  (字段名[(长度)]  [ASC |DESC]) 
                 );

 
#方法二:CREATE在已存在的表上创建索引
         CREATE  [UNIQUE | FULLTEXT | SPATIAL ]  INDEX  索引名 
                      ON 表名 (字段名[(长度)]  [ASC |DESC]) ;
 
 
#方法三:ALTER TABLE在已存在的表上创建索引
         ALTER TABLE 表名 ADD  [UNIQUE | FULLTEXT | SPATIAL ] INDEX
                              索引名 (字段名[(长度)]  [ASC |DESC]) ;
                              
#删除索引:DROP INDEX 索引名 ON 表名字;

******************************举例********************************************************
1.创建索引
    -在创建表时就创建(需要注意的几点)
    create table s1(
    id int ,#可以在这加primary key
    #id int index #不可以这样加索引,因为index只是索引,没有约束一说,
    #不能像主键,还有唯一约束一样,在定义字段的时候加索引
    name char(20),
    age int,
    email varchar(30)
    #primary key(id) #也可以在这加
    index(id) #可以这样加
    );
    -在创建表后在创建
    create index name on s1(name); #添加普通索引
    create unique age on s1(age);添加唯一索引
    alter table s1 add primary key(id); #添加主键索引,也就是给id字段增加一个主键约束
    create index name on s1(id,name); #添加普通联合索引
2.删除索引
    drop index id on s1;
    drop index name on s1; #删除普通索引
    drop index age on s1; #删除唯一索引,就和普通索引一样,不用在index前加unique来删,直接就可以删了
    alter table s1 drop primary key; #删除主键(因为它添加的时候是按照alter来增加的,那么我们也用alter来删)

 本节的参考文献在这里。

3. Hash、B树、B+树和红黑树的比较

hash 的查找时间复杂度是O(1)比B+的O(logn)查找时间更短,为什么索引不用hash?

可以从B+树索引的有序性,叶节点被双向链表连接,方便支持范围查找,以及分批加载至内存这几个方面回答

这和业务场景有关,如果只查找一个值的话,hash是一个很好的选择,单数据库经常会选择多条,这时候由于B+树索引有序,并且又有链表相连,它的查询效率比hash就快很多了。而且数据库中的索引一般是在磁盘上,数据量大的情况可能无法一次装入内存,B+树的设计可以允许数据分批加载,同时树的高度较低,提高查找效率

参考文献在这

为什么不用红黑树(可以从内存,以及树深度和IO次数方面讨论这个问题。)

 

  • 红黑树必须存在内存里的,数据库表太大了,存不进去。
  • 在大规模数据存储的时候,红黑树(二叉查找树)往往出现由于树的深度过大而造成磁盘IO读写过于频繁,进而导致效率低下的情况。B树可以有多个子女,从几十到上千,可以降低树的高度。
  • 磁盘IO代价主要花费在查找所需的柱面上,树的深度过大会造成磁盘IO频繁读写。根据磁盘查找存取的次数往往由树的高度所决定,红黑树查找一个节点最多要查logN层,每一层都是一个内存页。虽然你只是想找一个节点,但硬盘必须一次读一个页,那么一共logN次IO,消耗太大。

为什么不用B树(可以从叶节点是否存数据,占用内存空间大小和是否支持范围查询这三个方面解释。 )

 

  •  B+树的数据都集中在叶子节点,分支节点只负责索引。  b树的分支节点也有数据 。所以b+树的树高会小于B树,平均的Io次数会远大于 B+树。

(比如一个节点是一个页4096字节,其中每条数据128字节,那么一个节点只能存32个数据项,那么对应的孩子节点数最多为33个,这显然不够用。而b+树内部节点只作为导向作用,只存一个整数就可以(int型整数32位,消耗4个字节),4096/4=1024个数据项。这样b+树的每个节点的孩子数更多,整个树的高度就更低,大大增加查询效率。)

  • B+树索引节点没有数据。比较小。B树可以把索引完全加载至内存中。
  • B+树更擅长范围查询。叶子节点数据是按顺序放置的双向链表。  B树范围查询只能中序遍历,做不到范围查询。

B树、B+树和红黑树的参考文献在这。

4. 索引的优缺点

索引的优点

  • 通过创建索引,可以在查询的过程中,提高系统的性能
  • 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性
  • 在使用分组和排序子句进行数据检索时,可以减少查询中分组和排序的时间

索引的缺点

  • 创建索引和维护索引要耗费时间,而且时间随着数据量的增加而增大
  • 索引需要占用物理空间,如果要建立聚簇索引,所需要的空间会更大
  • 在对表中的数据进行增加删除和修改时需要耗费较多的时间,因为索引也要动态地维护

 

5. 什么时候该用索引?

  • 主键自动建立唯一索引

  • 在经常用在连接(join)的列上,这些列主要是一些外键,可以加快连接的速度

  • 在经常需要根据范围搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;

  • 频繁作为查询条件(经常where)的字段应该创建索引

  • 查询中经常(OrderBy)排序的字段(因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间)

  • 查询中统计(Count)或者分组(Groupby)的字段;

6. 什么时候不该用索引?

  • 查询中很少使用或者参考的列不应该创建索引。
  • 只有很少数据值的列也不应该增加索引。例如性别
  • 定义为text, image和bit数据类型的列不应该增加索引。这些列的数据量要么相当大,要么取值很少,不利于使用索引。
  • 频繁更新的字段不适合创建索引,因为每次更新不单单是更新记录,还会更新索引,保存索引文件
  • 表记录太少,不需要创建索引;
  • 经常增删改的表;

索引什么时候会失效?

 

  1. 如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)。注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引
  2. like查询是以%开头
  3. MySQL在使用 !=   >  <  between和 and这些时会全变扫描而使索引失效
  4. is null 和is not null也会失效
  5. 字符串不加单引号(where name=litao这样会失效)

  6. 索引列上的任何操作均会失效(计算,函数,类型转换)

  7. 尽量使用覆盖索引(避免select*,尽量使查询列和索引列一致)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章