MySql索引

MysqL索引

在公司入职后第一个工作安排就是看数据库,看表结构,看sql语句。索引在其中是一个很重要的角色。所以将索引进行一个系统的学习

索引是什么

官方介绍:帮助MysqL高效获取数据的数据结构。其目的就是快速查询检索数据。如果把数据库看成一本书的话,索引就是这本书的目录。

索引是个文件,它本身也很大,就像数据一样不可能完全存储在内存中。所以索引通常存储在文件中(可能存储在单独的索引文件中,也可能存储在数据文件中)。

索引有很多种(具体后文介绍)。认都是使用B+树的数据结构

索引的优缺点
优点
  • 使用索引可以加快数据的检索速度,提高数据检索效率,降低数据库IO的成本
  • 索引会对数据进行排序降低了数据排序的成本,就按照索引顺序排序相对于order by 语句而言,效率提升很多
缺点
  • 索引会占据磁盘空间
  • 索引的创建和维护需要大量时间。如果对表中结构进行修改的时候,那么索引也需要做相对的修改索引虽然会提高查询效率,但是会降低更新表的效率。比如每次对表进行增删改操作,MysqL不仅要保存数据,还有保存或者更新对应的索引文件

索引类型

主键索引+二级索引(起辅助的作用)(主键索引后的五个)

主键索引

索引列中的值必须是唯一的有,不允许有空值

普通索引(Index)

最基本的索引类型,没限制,普通索引的唯一作用就是为了快速查询数据,,允许在定义索引的列中插入重复值和空值

唯一索引(Unique Key)

唯一索引也是一种约束。唯一索引的属性列不能出现重复的数据,但是允许数据为 NULL,一张表允许创建多个唯一索引。 建立唯一索引的目的大部分时候都是为了该属性列的数据的唯一性,而不是为了查询效率。

全文索引(Full Text)

全文索引的目的是为了检索大文本数据中的关键字信息。只能在文本类型CHAR,VARCHAR,TEXT类型字段上创建全文索引。当字段长度比较大时,如果创建普通索引,在用like进行模糊查询时候效率低下。MysqL5.6 之前只有 MYISAM 引擎支持全文索引,5.6 之后 InnoDB 也支持了全文索引。

前缀索引(Prefix)

在文本类型如CHAR,VARCHAR,TEXT类列上创建索引时,可以指定索引列的长度,但是数值类型不能指定。

空间索引

为了解决空间数据查询与分析的难题,我们引入了空间索引,利用空间索引能够大幅度提高空间操作的效率和速度。在具体如何建立空间索引结构时,我们要根据实际情况和实际需求来建立相对应的空间索引类型,进而帮助我们解决实际问题。MysqL在5.7之后的版本支持了空间索引,而且支持OpenGIS几何数据模型。MysqL在空间索引这方面遵循OpenGIS几何数据模型规则。

按照索引列数量分类
单列索引
组合索引

组合索引的使用,遵循最左前缀匹配原则。一般情况下在条件允许的情况下使用组合索引替代多个单列索引使用。

最左前缀匹配原则

通常我们在建立联合索引的时候会发现,无论是Oracle还是 MysqL 都会让我们选择索引的顺序,比如我们想在a,b,c三个字段上建立一个联合索引,我们可以选择自己想要的优先级,(a、b、c),或是 (b、a、c) 或者是(c、a、b) 等顺序

比如索引index:(a,b,c)是a,b,c三个字段的联合索引,下列sql执行时都无法命中索引index;

select * from table where c = '1';
select * from table where b ='1' and c ='2';

一下三种情况会走索引

select * from table where a = '1';
select * from table where a = '1' and b = '2';
select * from table where a = '1' and b = '2'  and c='3';

MysqL 建立多列索引(联合索引)有最左匹配的原则,即最左优先:
如果有一个 2 列的索引 (a, b),则已经对 (a)、(a, b) 上建立了索引;
如果有一个 3 列索引 (a, b, c),则已经对 (a)、(a, b)、(a, b, c) 上建立了索引;

违背最左原则导致索引失效的情况

1 查询条件中,缺失优先级最高的索引 “a”
  当 where b = 2 and c = 3 这种没有以 a 为条件来检索时;B+树就不知道第一步该查哪个节点,从而需要去全表扫描了(即不走索引)。因为建立搜索树的时候 a 就是第一个比较因子,必须要先根据 a 来搜索,进而才能往后继续查询b 和 c,这点我们通过上面的存储结构图可以看明白。

2 查询条件中,缺失优先级居中的索引 “b”
  当 where a =1 and c =3 这样的数据来检索时;B+ 树可以用 a 来指定第一步搜索方向,但由于下一个字段 b 的缺失,所以只能把 a = 1 的数据主键ID都找到,通过查到的主键ID回表查询相关行,再去匹配 c =3的数据了,当然,这至少把 a = 1 的数据筛选出来了,总比直接全表扫描好多了。

3 对于联合索引,MysqL 会一直向右匹配直到遇到范围查询(> , < ,between,like)就停止匹配。比如 a = 3 and b = 4 and c > 5 and d = 6,如果建立的是(a,b,c,d)这种顺序的索引,那么 d 是用不到索引的,但是如果建立的是 (a,b,d,c)这种顺序的索引的话,那么就没问题,而且 a,b,d 的顺序可以随意调换。
= 和 in 可以乱序,比如 a = 3 and b = 4 and c = 5 建立 (a,b,c)索引可以任意顺序。
如果建立的索引顺序是 (a,b)那么直接采用 where b = 5 这种查询条件是无法利用到索引的,这一条最能体现最左匹配的特性。

索引的数据结构

Hash表
Hash表,在Java中的HashMap,TreeMap就是Hash表结构,以键值对的方式存储数据。我们使用Hash表存储表数据Key可以存储索引列,Value可以存储行记录或者行磁盘地址。Hash表在等值查询时效率很高,时间复杂度为O(1);但是不支持范围快速查找,范围查找时还是只能通过扫描全表方式。

显然这种并不适合作为经常需要查找和范围查找的数据库索引使用。

二叉查找树

每个节点最多有2个分叉,左子树和右子树数据顺序左小右大。

这个特点就是为了保证每次查找都可以这折半而减少IO次数,但是二叉树就很考验第一个根节点的取值,因为很容易在这个特点下出现我们并发想发生的情况“树不分叉了”,这就很难受很不稳定。

显然这种情况不稳定的我们再选择设计上必然会避免这种情况的

平衡二叉树

平衡二叉树是采用二分法思维,平衡二叉查找树除了具备二叉树的特点,最主要的特征是树的左右两个子树的层级最多相差1。在插入删除数据时通过左旋/右旋操作保持二叉树的平衡,不会出现左子树很高、右子树很矮的情况。

使用平衡二叉查找树查询性能接近于二分查找法,时间复杂度是 O(log2n)。查询id=6,只需要两次IO。

依然存在一些问题:

时间复杂度和树高相关。树有多高就需要检索多少次,每个节点的读取,都对应一次磁盘 IO 操作。树的高度就等于每次查询数据时磁盘 IO 操作的次数。磁盘每次寻道时间为10ms,在表数据量大时,查询性能就会很差。(1百万的数据量,log2n约等于20次磁盘IO,时间20*10=0.2s)

平衡二叉树不支持范围查询快速查找,范围查询时需要从根节点多次遍历,查询效率不高。

B树:改造二叉树

MysqL的数据是存储在磁盘文件中的,查询处理数据时,需要先把磁盘中的数据加载到内存中,磁盘IO 操作非常耗时,所以我们优化的重点就是尽量减少磁盘 IO 操作。访问二叉树的每个节点就会发生一次IO,如果想要减少磁盘IO操作,就需要尽量降低树的高度。那如何降低树的高度呢?

为了最大化利用一次IO空间,一个简单的想法是在每个节点存储多个元素,在每个节点尽可能多的存储数据。这样就将二叉树改造成了多叉树,通过增加树的叉树,将树从高瘦变为矮胖。树的高度只需要2层就可以,也就是说只需要2次磁盘IO就可以查询到数据。磁盘IO次数变少了,查询数据的效率也就提高了。

这种数据结构我们称为B树,B树是一种多叉平衡查找树,主要特点:

1 B树的节点中存储着多个元素,每个内节点有多个分叉。

2 节点中的元素包含键值和数据,节点中的键值从大到小排列。也就是说,在所有的节点都储存数据。

3 父节点当中的元素不会出现在子节点中。

4 所有的叶子结点都位于同一层,叶节点具有相同的深度,叶节点之间没有指针连接。

依然存在可以优化的地方:

B树不支持范围查询快速查找,你想想这么一个情况如果我们想要查找10和35之间的数据,查找到15之后,需要回到根节点重新遍历查找,需要从根节点进行多次遍历,查询效率有待提高。

如果data存储的是行记录,行的大小随着列数的增多,所占空间会变大。这时,一个页中可存储的数据量就会变少,树相应就会变高,磁盘IO次数就会变大。

B+树:改造B树

B+树,作为B树的升级版,在B树基础上,MysqL在B树的基础上继续改造,使用B+树构建索引。B+树和B树最主要的区别在于非叶子节点是否存储数据的问题

  • B树:非叶子节点和叶子节点都会存储数据。

  • B+树:只有叶子节点才会存储数据,非叶子节点至存储键值。叶子节点之间使用双向指针连接,最底层的叶子节点形成了一个双向有序链表。

B+树可以保证等值和范围查询快速查找,MysqL的索引就采用了B+树的数据结构。

联合索引的使用

联合索引,在建立索引的时候,尽量在多个单列索引上判断下是否可以使用联合索引。联合索引的使用不仅可以节省空间,还可以更容易的使用到索引覆盖。试想一下,索引的字段越多,是不是更容易满足查询需要返回的数据呢。比如联合索引(a_b_c),是不是等于有了索引:a,a_b,a_b_c三个索引,这样是不是节省了空间,当然节省的空间并不是三倍于(a,a_b,a_b_c)三个索引,因为索引树的数据没变,但是索引data字段的数据确实真实的节省了。

联合索引的创建原则,在创建联合索引的时候因该把频繁使用的列、区分度高的列放在前面,频繁使用代表索引利用率高,区分度高代表筛选粒度大,这些都是在索引创建的需要考虑到的优化场景,也可以在常需要作为查询返回的字段上增加到联合索引中,如果在联合索引上增加一个字段而使用到了覆盖索引,那我建议这种情况下使用联合索引。

联合索引的使用

考虑当前是否已经存在多个可以合并的单列索引,如果有,那么将当前多个单列索引创建为一个联合索引。
当前索引存在频繁使用作为返回字段的列,这个时候就可以考虑当前列是否可以加入到当前已经存在索引上,使其查询语句可以使用到覆盖索引。

相关文章

显卡天梯图2024最新版,显卡是电脑进行图形处理的重要设备,...
初始化电脑时出现问题怎么办,可以使用win系统的安装介质,连...
todesk远程开机怎么设置,两台电脑要在同一局域网内,然后需...
油猴谷歌插件怎么安装,可以通过谷歌应用商店进行安装,需要...
虚拟内存这个名词想必很多人都听说过,我们在使用电脑的时候...