经常在开发中碰到同事说,数据查询好慢,第一个反应就是给表加个索引。从而引发想去探索下我们常说的索引究竟是什么?难道只需要加个索引就能解决数据库查询问题吗?
带着这个问题我们开始探究MySQL中的索引究竟是什么,它能帮助我们做些什么。
在现有程序业务中,数据库作为存储的重要一环,不可或缺,而对于数据库的操作无外乎是增删改查,但随着数据量的增加,数据库的性能就成为最重要的一环,数据查询不能慢,数据查询一慢,用户体验就会差。
如何在保证数据存储中的增删改查效率呢?就成了一个必不可少的设计。
在Mysql这样的数据存储中总是少不了一个东西—>索引,索引就类似于我们看书的目录,使用书籍的目录可以帮助快速的定位到知识点的页数,而索引也是同样的目的,快速检索到数据。
那就可以总结出索引的目的:提高数据的检索速度。
既然索引有提高检索的速度,那就给数据库的查询操作都加上索引,让他们飞快的运行,这事还真不能急,为啥?数据库的索引种类有好多种,万一索引用的不对,引发的不是加快数据库的运行,而是众多的慢查询会将整个数据库拖垮。
用于提高读写效率的数据结构种类这么多,那我们来了解下数据库中常见的索引类型都有哪些。
索引类型 | 哈希索引 | 二叉树 | 跳表 | B+Tree |
---|
哈希索引简单来说就是Key-Value模型,我们只要通过给定的Key就可以查找到对应的Value,十分快速方便。
不过你要明白哈希索引是通过哈希函数对Key进行计算,换算成数据存储位置,随着数据量的增加,不可避免会出现不同的Key经过哈希函数计算后出现相同的数据存储位置。
这种情况怎么解决呢?业界通用的方式是当出现位置一样的数据结果,会在该结果后面链接一个链表,将相同的数据放入到链表中。
更进一步当相同位置中的数据越来越多,查询数据时会将链表中的数据遍历,速度也是慢,这时候可以采用将链表进行树化,二叉树的查找速度还是很快的。
哈希索引只适合用来查找等值的数据,而不是适合范围索引,排序等操作。常见的哈希索引是在Redis中。
数据结构中存在树数据结构,虽然树的结构多种多样,但是常用的数据结构是二叉树,二叉树是拥有两个分叉的树,分别为左子节点与右子节点。以此类推,动物中有八爪鱼,同样的也存在八叉树,你可以想象八叉树是什么样子。
二叉树的特点是,左节点的值<父节点<右节点。如果要查找到一个值就可以按照子节点的顺序进行查找.
随着数据量的增大,二叉树的高度也会主键递增,数据库存储的数据并不是都放到内存中,而是要放到磁盘上,磁盘的访问速度是比内存慢几十倍。
现在假如一个树高30,每次搜索树一次就需要访问一次硬盘,一次访问磁盘速度假设是10ms,树高30至少需要访问磁盘30次才能获取到数据,30*10=300ms。
如果数据更多,树高到100,获取一次数据成本就很高了。
为了解决这个问题,可以使用N叉树的方式来降低树的高度,减少访问磁盘的次数,这样就能提高效率。
跳表是建立在多层级链表上的数据结构,通过一层层的链表查询就提高了检索数据的效率。
Mysql中索引的实现是建立在数据库引擎上的,而在Mysql中有多个数据库引擎,常用的数据库引擎是InnoDB.
InnoDB引擎索引实现是使用B+Tree索引模型,其实还有一种BTree模型,B+Tree是建立在BTree基础上发展的。
B+Tree可以认为是BTree的改进版本:
注意子节点与叶子节点是不同的概念。把没有子节点的节点叫做叶子节点
数据库中每一个索引都能对应到一颗B+树,一个表是可以存在多个B+树。
不管是B+Tree还是BTree都是利用多叉树(该树有多少叉是根据页的大小进行计算好的,索引会涉及到新增删除,同样的就会涉及到页的分裂与合并),保证不把所有的索引数据放入到内存上,降低磁盘的访问次数加快数据访问。
Mysql中常用Innodb引擎,组织数据库索引的方式就是B+Tree。
B+Tree是索引组织表,那在B+Tree有多少种索引的类型呢?
从不同的方向划分可以划分为不同的类型。
主要为普通索引,主键索引,唯一索引,前缀索引,全文索引,哈希索引。
普通索引就是我们常用的索引创建-> 创建单个索引,相关语句如下
alter table table_name add index index_name(column);
drop index index_name on table_name;
ALTER TABLE table_name DROP INDEX index_name
主键索引是在普通索引的基础上增加两种约束条件分别为唯一和不能为空。主键索引在Innodb中用来维护索引组织的性质,所以,在使用Innodb引擎时,建议你的表都设置主键。
创建主键可以在创建的表的时候指定 primary key(‘id’),也可以创建联合主键primary key (‘id’,‘name’).
创建主键的相关SQL
# 当表里面没有主键索引时,增加主键索引
ALTER TABLE table_name ADD PRIMARY KEY ( `id` )
# 删除主键索引
ALTER TABLE table_name DROP INDEX name_index
唯一索引时在普通索引的基础上增加唯一的约束,在插入相关数据时,会检查该索引数据是否已经存在数据库中。
使用下面的创建语句创建:
# 创建唯一索引
ALTER TABLE table_name ADD UNIQUE (`column`)
# 删除索引
drop index index_name on table_name;
字符串在编程中经常遇到的,比如常用的邮箱,一些业务场景中需要对某些字符串的前缀进行匹配。
这就涉及到一个问题,不能使用索引的话,就只能进行全表扫描。数据量一大,该方式就会成为性能的瓶颈。
数据库中的前缀索引就是解决字符串前缀匹配的问题。
# 创建前缀索引
alter table table_name add index index_name(columns(6));
# 删除索引
drop index index_name on table_name;
# 怎么计算前缀索引设计几个字符 使用下列语句进行估算
select count(distinct 列名)/count(*)as a,COUNT(DISTINCT left(列名,100)) as b, COUNT(DISTINCT left(列名,110)) as c from 表名
前缀索引有一个缺点就是无法使用覆盖索引的优化,必须回表查询。
全文索引是用来解决Mysql中文本匹配慢的问题,常使用like模糊搜索%内容%,没法用到前面列举的索引,这时候就可以尝试使用全文索引来解决该问题。
相关SQL文件看下
create fulltext index table_name
on index_name(column,column);
alter table table_name
add fulltext index index_name(column,column);
注意一点的是全文索引是有自己的匹配语法,使用match和against关键字来进行匹配。
select * from table_name where match(column,column) against('xxx xxx');
从个数区分就是该索引邮几个列组成。当有多个列构成就是联合索引
单个索引的介绍不用多说,这里主要说下联合索引。
我们知道B+Tree树这种形式的索引结构是可以使用最左前缀,来定位记录。十分恰当的联合索引就需要使该规则才能发挥出强大的作用。
顺便引出的问题就是我们在创建联合索引的时候应该怎么安排索引内的字段顺序?
当当前表是新建立的,还没有其他索引可以根据业务需求进行直接创建;如果表中已经存在其他索引,那可以通过调整顺序帮助减少索引的创建。
每次创建一个新的索引,就会增加一部分的索引存储空间,随着数据量的增加,索引的存储也会暴涨,所以在创建索引时,都需要考一个空间占用的原则。
当有一个大字段和小字段组合成联合索引时,大字段索引放在联合索引的前面。
比如现在需要根据邮件和年龄查询数据,但还有根据age以及email单独查询的需求。
一般第一个反应就是创建三个索引,age,name,(name,age)/(age,name)。
而一般email的长度是大于age的,在有最左前缀的原则下,联合索引第一个字段单独查询是可以使用索引。则这里选择创建的索引就是age,name,(name,age)。
看一些数据库资料总是聚簇索引,非聚簇索引,这两种方式跟主键索引,普通索引又有什么区别?
其实就是一类内容,只是根据分类的方式不同,叫的名字不同而已。聚簇索引与非聚簇索引是指在磁盘上对数据的组织结构不同。
聚簇索引可认为是磁盘将实际数据按照定物理地址进行顺序存放,并且与索引的顺序是一致的。那么当索引是相邻的,对应的数据一定也是按照相邻的顺序存放。
磁盘对于顺序读取速度比磁盘随机读取的速度要快很多;正因为聚簇索引是按照物理顺序进行存储,那一个表只能有一个聚簇索引,该索引在Mysql中是主键索引,当然主键索引也是可以包含多个列的。
其他类型的索引都是被称为非聚簇索引。
使用非聚簇索引查询数据,目的是先查到对应的聚簇索引也就是主键索引。通过主键索引从而查询到对应的数据。在这个过程中还涉及到两个技术,分别为回表以及索引下推。
回表很见到就是通过其他索引查到对应的主键值,再使用主键值去表里面再检索一遍数据。
那有没有不用回表?有的,在查询的数据的时候,查询的索引中已经包含要的字段,就不需要再使回表使用主键查询数据,这句话可能有点蒙,看下SQL你就明白了。
# 设置userid为普通索引
# 不使用覆盖索引,需要回表查询
select * from user where userid=1
# 使用了覆盖索引,这是因为userid的索引列上叶子节点就是存储的主键id,不需要再回表
select ID from user where userid=1;
# 使用联合索引也是可以做到的(userid,name)做一个联合索引,索引列上有对应的值,则不需要再回表查询
select name from user where userid =1
在Mysql5.6版本以前,查询到的数据每条都需要重新回表查询一次,而在5.6版本增加索引下推技术后,可以直接在索引列中过滤掉不需要数据,减少回表的次数。
注意该技术需要使用范围是联合索引(age,sex)
select * from user where 1020 and sex=1;
首先使用索引查询到年龄满足第一条大于10小于20的人,同时索引列可以直接进行判断该用户性别是不是满足1(男性),是就继续,不是该条记录过滤掉。
而在5.6以前满足大于10小于20后,根据读取到主键数据再进行对比。回表的次数自然比使用索引下推技术版本多。
本章从一个小问题引发了对索引的探索,包含对现有的数据库中常用的几个索引技术进行介绍,明白为什么Mysql会选择B+Tree作为索引的检索方式,并通过此方式梳理了Mysql中现有的索引技术。
希望这边索引文章能帮助到你对索引的理解。