Mysql索引学习笔记

https://www.jianshu.com/p/ace3cd6526c4
推荐up主https://space.bilibili.com/377905911

系列文章目录和关于我

一丶什么是索引

索引是存储引擎快速找到记录的一种数据结构。数据库中的数据可以理解成字典中的单词,而索引就是目录,显而易见这是一种空间换时间的做法,目录占用了空间,但是加快了我们找到单词的速度,正如索引需要空间存储,但是利用索引我们可以快速的找到想要的数据。

InnoDB存储引擎存在几种常见的索引:

  • B+树索引
  • 全文索引
  • 哈希索引

本文主要讨论 B+树索引

二丶索引的数据结构

可以加快查找速度的数据结构很多,为什么mysql使用 B+树来实现昵,换句话说哈希表,有序数组,跳表,平衡二叉搜索树,B-树等都可以优化搜索效率,为什么偏偏使用 B+树

1.哈希表

哈希表,可以联想Java中的HashMap,在HashMap源码学习中,我们了解到Hash表的数据结构。如下图

Mysql索引学习笔记

哈希表通过hash算法将key映射到数组对应的下标进行存储,不可避免的会产生hash冲突(多个不同的key散列到相同的数组下标中),解决hash冲突常用拉链法,顾名思义,就是把相同hash值的节点组成链表串起来。在根据key查找value的过程中,只需要再次使用相同的hash算法那么就能拿到对应的数组下表,然后遍历链表找到目标值即可,查找的效率是o(1)。

明明存在链表需要遍历为什么说时间复杂度o(1)
首先hash算法计算是常数时间,
hash表会在需要的时候进行扩容,
维持链表长度尽量在一个常数范围,从而保证遍历常数个链表节点

mysql中存在 &#x81EA;&#x9002;&#x5E94;&#x54C8;&#x5E0C;&#x7D22;&#x5F15;,由innodb存储引擎自己控制,利用查找O(1)的性质优化等值查询。我们可以看出,hash表并不适合范围查询,对于 Id<10< code>&#x8FD9;&#x79CD;&#x8303;&#x56F4;&#x67E5;&#x8BE2;&#x53EA;&#x80FD;&#x904D;&#x5386;hash&#x8868;&#x4E2D;&#x6BCF;&#x4E00;&#x4E2A;&#x6570;&#x636E;&#xFF0C;&#x76F8;&#x5F53;&#x4E8E;&#x8981;&#x8FDB;&#x884C;&#x4E00;&#x6B21;&#x5168;&#x8868;&#x626B;&#x63CF;&#x3002;&#x6211;&#x8FD8;&#x6709;&#x4E00;&#x4E2A;&#x60F3;&#x6CD5;&#xFF1A;&#x4ECE;&#x6269;&#x5BB9;&#x7684;&#x89D2;&#x5EA6;&#x770B;&#xFF0C;&#x6BCF;&#x6B21;&#x6269;&#x5927;&#x6570;&#x7EC4;&#x5927;&#x5C0F;&#x540E;&#x90FD;&#x9700;&#x8981;&#x79FB;&#x52A8;&#x5143;&#x7D20;&#x5230;&#x65B0;&#x7684;&#x6570;&#x7EC4;&#x7A7A;&#x95F4;&#x4E2D;&#xFF0C;&#x8FD9;&#x90E8;&#x5206;&#x7684;&#x590D;&#x5236;&#x79FB;&#x52A8;&#x7684;&#x5F00;&#x9500;&#x4E5F;&#x8BB8;&#x4E5F;&#x662F;hash&#x8868;&#x4E0D;&#x5408;&#x9002;&#x7684;&#x539F;&#x56E0;&#xFF08;redis&#x4E3A;&#x4E86;&#x89E3;&#x51B3;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;&#xFF0C;&#x4F7F;&#x7528;<code>&#x6E10;&#x8FDB;&#x5F0F;hash</code>&#x7684;&#x65B9;&#x5F0F;&#xFF0C;&#x5728;&#x6269;&#x5BB9;&#x7684;&#x65F6;&#x5019;&#x751F;&#x6210;&#x66F4;&#x5927;&#x7684;&#x6570;&#x7EC4;&#xFF0C;&#x4F46;&#x4E0D;&#x662F;&#x4E00;&#x6B21;&#x79FB;&#x52A8;&#x6240;&#x4EE5;&#x6570;&#x636E;&#xFF0C;&#x800C;&#x662F;&#x63D2;&#x5165;&#x7684;&#x65B0;&#x5143;&#x7D20;&#x90FD;&#x653E;&#x5230;&#x65B0;&#x6570;&#x7EC4;&#xFF0C;&#x8001;&#x6570;&#x7EC4;&#x4F7F;&#x7528;&#x5230;&#x7684;&#x6570;&#x636E;&#x624D;&#x4F1A;&#x6162;&#x6162;&#x79FB;&#x52A8;&#x5230;&#x65B0;&#x6570;&#x7EC4;&#xFF09;redis&#x57FA;&#x4E8E;&#x5185;&#x5B58;&#x7684;&#x6570;&#x636E;&#x5E93;&#x90FD;&#x9700;&#x8981;&#x901A;&#x8FC7;&#x6E10;&#x8FDB;&#x5F0F;hash&#x4F18;&#x5316;&#x6269;&#x5BB9;&#x64CD;&#x4F5C;&#xFF0C;&#x57FA;&#x4E8E;&#x78C1;&#x76D8;&#x7684;mysql&#x82E5;&#x4F7F;&#x7528;hash&#x5C06;&#x60E8;&#x4E0D;&#x5FCD;&#x7779;<!--10<-->

2.有序数组

有序数组就是数据中元素有序,正因为有序,所以其在范围查找上非常优秀,正因为要维持有序,在更改数据的时候,也许需要移动大量数组元素(比如插入一个较小的值,大于此值的数据都需要后移动),所以有序数据只适用于静态数据(比如2020年人口信息,这种不会改变的数据)

3.跳表

Mysql索引学习笔记

为了解决有序数组需要移动元素的问题,我们可以使用链表来维护元素,从而使更改元素效率为o(1),但是链表的查找非常慢。由于链表整体是有序的,那么我们可以使用二分查找优化查找效率,如上我们建立多级的节点,在查找的时候我们首先通过多级的索引依次找到最下层。对于范围查找,由于底层数据是有序的,查找 id<7< code>&#x7684;&#x6570;&#x7EC4;&#xFF0C;&#x9996;&#x5148;&#x6211;&#x4EEC;&#x627E;&#x5230;<code>id=7</code>&#x7136;&#x540E;&#x5411;&#x5DE6;&#x904D;&#x5386;&#x96C6;&#x5408;&#xFF08;&#x53EF;&#x4EE5;&#x628A;&#x8DF3;&#x8868;&#x6700;&#x4E0B;&#x9762;&#x4E00;&#x5C42;&#x4F18;&#x5316;&#x4E3A;&#x53CC;&#x5411;&#x94FE;&#x8868;&#xFF0C;&#x4ECE;&#x800C;&#x8BA9;&#x8303;&#x56F4;&#x67E5;&#x627E;&#x901F;&#x5EA6;&#x4E5F;&#x5F88;&#x5FEB;&#xFF09;<!--7<-->

哪为什么不使用跳表来做索引昵?

跳表是链表结构,一条数据一个结点,如果最底层要存放2kw数据,且每次查询都要能达到二分查找的效果,2kw大概在2的24次方左右,所以,跳表大概高度在24层左右。最坏情况下,这24层数据会分散在不同的数据页里,也即是查一次数据会经历24次磁盘IO。

Mysql索引学习笔记

4.平衡二叉搜索树

二叉搜索树,即左子树小于根,根小于右子树,这种结构在查找的时候可以进行二分,根据根节点的值就可以确定期望的数据在左树还是右树。

Mysql索引学习笔记

但是二叉搜索树在插入,删除节点的时候可能出现树极度不平衡的情况,出现树退化成链表。

Mysql索引学习笔记

这个时候就需要维持树的平衡——AVL:在满足二叉搜索树的条件下,要求任何节点的两个子树高度差不超过1。平衡二叉树的查找是趋近于O(log(N)),但是需要维护一颗树为AVL需要进行左旋,右旋的操作,更新的时间复杂度也是 O(log(N))。

为什么不使用AVL做索引:节点存储的数据内容太少。因为操作系统和磁盘之间一次数据交换是以页为单位的,一页 = 4K,即每次IO操作系统会将4K数据加载进内存。但是,在二叉树每个节点的结构只保存一个关键字,一个数据区,两个子节点的引用,并不能够填满4K的内容。幸幸苦苦做了一次的IO操作,却只加载了一个关键字,在树的高度很高,恰好又搜索的关键字位于叶子节点或者支节点的时候,取一个关键字要做很多次的IO。

5.B-树,B+树

B-树就是B树,英文是B-Tree,所以国内有许多人称之为B-树。B树和B+树是 多路平衡查找树,之所以 &#x591A;&#x8DEF;,是为了契合磁盘的io操作——操作系统和磁盘之间一次数据交换是以页为单位的,多路能让读取一页能获取更多的数据,让树的高度更低。

Mysql索引学习笔记

Mysql索引学习笔记

上面两图,我们可以看出B树和B+树的区别

  1. B+树叶子节点使用双向指针串联起来,这让B+树相比于B树更加适合范围查找
  2. B+树非叶子节点并不存数据,所以每次查找数据都必须遍历到叶子节点,时间复杂度稳定为O(logN),B-树在运气好的时候可以在根节点直接拿到数据。但是正是因为非叶子节点不存储数据,可以让一次磁盘读取一页中包含的索引数据更多, 每个节点能索引的范围更大更精确,让我们可以更改定位到期望的数据。由于B+树的叶子节点的数据都是使用链表连接起来的,而且他们在磁盘里是顺序存储的,所以当读到某个值的时候,磁盘预读原理就会提前把这些数据都读进内存,使得范围查询和排序都很快

B+树在更改数据的时候,为了保证树的平衡可能存在节点的分裂和合并,所以我们一般建议使用自增主键,在插入的时候,不会频繁的发生节点的分裂。

三丶聚集索引和非聚集索引

InnoDB存储引擎是索引组织表——表中的数据按照主键顺序存放。非聚集索引也称做辅助索引,无论是聚集还是非聚集,其原理都是一颗B+树,叶子节点都存储数据,不同的是聚集索引叶子节点存储的是一整行的数据,非聚集索引叶子节点存储的是聚集索引值(主键值)。

如果数据表定义了主键,那么这个索引就是聚集索引,如果没有定义主键,mysql会选择该表的第一个非空唯一的索引构建聚集索引,如果都没有那么mysql会生成一个隐藏的列(6字节的列,并且插入自增)

自增主键会把数据自动向后插入,避免了插入过程中聚集索引节点分裂的问题。节点分裂会带来大范围的数据物理移动,带来磁盘IO的性能损耗,并且我们一般建议尽量不要改动主键,主键的更改也会带来page分裂,产生碎片。

四丶回表查询

Mysql索引学习笔记

如上图,假如我们有一张表存在三个字段 id,age,name我们在id上建立了主键索引,这时候id主键索引也是聚集索引,在age上建立了普通索引,这时候age索引就是非聚集索引。如果我们执行 select * from table where age=1这时候先走age索引(如果数据量较大,数据量少直接全表扫描了)那么会找到对应的主键id,继续到主键id索引中找到目标数据,这个操作叫做 回表

这就是为什么根据主键查找快于根据其他索引列查找,因为如果其他索引列没有包含我们 select语句中需要的列(如果是 select id from table where age<10< code>,&#x90A3;&#x4E48;age&#x7D22;&#x5F15;&#x662F;&#x53EF;&#x4EE5;&#x8986;&#x76D6;&#x5230;&#x9700;&#x8981;&#x7684;&#x6570;&#x636E;&#x7684;(&#x53F6;&#x5B50;&#x8282;&#x70B9;&#x5B58;&#x50A8;&#x4E86;id)&#xFF0C;&#x90A3;&#x4E48;&#x4E5F;&#x4E0D;&#x4F1A;&#x56DE;&#x8868;&#xFF09;&#xFF0C;&#x90A3;&#x4E48;&#x4F1A;&#x8D70;&#x4E3B;&#x952E;&#x7D22;&#x5F15;&#x62FF;&#x5230;&#x9700;&#x8981;&#x7684;&#x6570;&#x636E;&#xFF0C;&#x591A;&#x4E86;&#x4E00;&#x6B65;&#x56DE;&#x8868;&#x64CD;&#x4F5C;&#x3002;<!--10<-->

这里我们也可以看到为什么建议使用 select *,这意味着查找所有列,如果配合上普通索引,那么大概率这个普通索引不会覆盖到索引列,导致需要回表查询。并且 select*这种”我全都要”大概率会查询到我们不需要的列,造成不必要的网络资源消耗,增加不必要的io,增加不必要的内存消耗。

五丶联合索引

Mysql索引学习笔记

联合索引是指对表上的多个列建立索引,如上图表存在四个字段 id,address,name,age,我们在name和age上建立索引,上图我们粗略的展示了联合索引的B+树结构。我们可以观察到在叶子节点中name是有序的,但是age无序,联合索引是按照索引定义的顺序排序的,这就导致 select xxx from table where name='b'是可以根据上面定义的联合索引查找数据的,但是`select xxx from table where age=12是无法走上面定义的联合索引的。这就是常说的最左前缀匹配原则的原理。

  • 联合索引可以减少回表 如果我们执行 select age,id from table where name='a' and age=10,这个时候由于我们定义的聚集索引一级包含了需要的数据就不需要进行回表操作了(这其实也被称为覆盖索引,即非聚集索引中可以查询到全部需要的列,那么就不需要走聚集索引回表查询数据)
  • 联合索引可以优化排序 上图中的联合索引,我们可以看到,名称相同的节点,其年龄是有序的 也就是说 select * from table where name='a' order by age这个语句将避免多一次的排序操作( select* from table where id=1 order by age会走主键索引拿到所有符合数据进行排序,这里说的避免一次排序操作指拿到的数据本身就是有序的 所有不需要再次排序)
    Mysql索引学习笔记
  • 如何安排联合索引的顺序 第一原则是,如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的,比如业务中存在两个高频查询,根据name,以及根据name查询后根据age排序,这个时候我们应该建立 name age的联合索引,上面我们说过 name,age的所有其中name是有序的,age只在name相同的情况下才是有序的,这样可以减少建立name的普通索引,并且优化排序,甚至利用索引下推减少回表。如果还存在根据age进行的查询,那么需要单独维护一个age的普通索引

六丶索引建立原则

1.为搜索,排序,分组的列建立索引

一般只为出现在 where后面的列,连接子句中的列,出现在 order by,或者 group by的列进创建索引。不要无脑建立索引,索引是需要存储在磁盘上的,占用空间,并且在新增,删除,修改的时候还需要维护索引,是需要时间的。

比如 select xxx from table where name= 'a' order by user_no,这条查询语句可以选择在name上建立索引,也可以选择在user_no 上建立索引,后者可以优化排序。

2.考虑列中不重复的个数建立索引

select xxxx from table where sex=1 这里不要为 sex性别建立索引,性别通常只有男和女,为其建立索引,b+树只有两个节点,查找之后还要对一半的进行回表,不如直接走全表扫描

3.索引列尽可能小

mysql基本数据类型十分丰富,整数类型有 tinyint, mediumint, intbigint,我们因该尽量使用占用字节数小的数据类型,这样可以让每次读取磁盘获取一页的数据,可以获得更多的范围信息

4.为列前缀进行索引

比如说有英文名可能很长,每次都是根据FirstName 进行like查找,这时候可以选择为列的前10个字符建立索引(alter table user add index idx_name(name(10)))。但是十个字符之后将无法使用索引。

5.合理的建立覆盖索引

在聚集索引小节中,我们总结了聚集索引的好处,减少回表,优化排序,索引下推。

6.不要在uuid上建立索引

首先uuid占用字节大,导致每一页范围信息少,并且uuid无序,这会导致插入数据的时候节点的分裂。这里也说明了自增主键优秀的点,不会频繁的节点分裂,并且不要修改主键,避免不必要的节点分裂。相比于uuid作为主键,不如使用分布式自增主键生成的方案

7.存在联合索引的情况下,不要重复建立索引

存在 name,age的联合索引,那么不需要再为name单独建立索引了,但是可以为age建立索引,原理在联合索引中进行了讲解。

七丶索引失效

Mysql索引学习笔记

上图是mysql的基本架构,其中存在优化器,其作用是不改变sql执行j结果的情况下,让sql更加简单,并且根据成本分析,制定执行计划。是否走索引,走什么索引也是优化器来决定的(sql中可以提示使用什么索引,强制使用某一个索引)。

Mysql索引学习笔记

Mysql索引学习笔记

常见索引失效的原因有

  1. 不满足最左前缀原则 如果存在 a,b,c的联合索引, select * from table where b=2 and a=1这种时候还是可能走联合索引的,mysql会优化语句,但是 select * from table where b=1 and c=2是无法走联合索引的,因为b,c在b+树中整体无序
  2. 使用了select 使用select需要回表,也许mysql优化器评估后觉得走非聚集索引,不如直接全表扫描
  3. 索引列上有计算,索引列上使用了函数
  4. 类型不匹配 select * from table where name = 123,可以理解成mysql把sql语句添加了类型转换函数,导致无法走索引
  5. like查询左边有% 以xxx开头是可以走索引的,因为是有序的,但是以xxx结尾和包含xxx是无法走索引的。
  6. order by 使用了联合索引中不存在的列,或者顺序不符合最左前缀匹配
  7. group by 使用了联合索引中不存在的列,或者顺序不符合最左前缀匹配

Original: https://www.cnblogs.com/cuzzz/p/16684456.html
Author: Cuzzz
Title: Mysql索引学习笔记

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/713963/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 采挖九里香树桩,牢记移植这4点,爱成活长得旺

    前言:笔者在4月23日、28日的文章中介绍了九里香树桩的养坯、造型制作方法。随着秋分、霜降即将到来,喜欢树桩的北方盆友对采挖、栽植九里香生桩的流程仍有疑问,所以本期文章与盆友们共同…

    技术杂谈 2023年5月31日
    0123
  • (动态规划)最长递增子列

    先来看问题, 最长递增子列。 即数组中按顺序拿出n个数,(按照原来的顺序)该子数列为递增数列。 例如:1 2 3 -1数列最后结果为3.最对应数列很显然为1 2 3 (注,只输出长…

    技术杂谈 2023年7月24日
    051
  • WTL中最简单的实现窗口拖动的方法(转)

    目前,很多基于对话框的应用程序中对话框都是不带框架的,也就是说对话框没有标题栏。众所周知,窗口的移动都是通过鼠标拖动窗口的标题栏来实现的,那么现在应用程序中的对话框没有了标题栏,用…

    技术杂谈 2023年5月31日
    075
  • Python 字符串操作常用知识点

    引切片:通过索引(索引:索引:步长)截取字符串的一段,形成新的字符串(原则就是顾头不顾腚)。 a = ‘ABCDEFGHIJK’ print(a[0:3]) # print(a[:…

    技术杂谈 2023年6月21日
    081
  • shopify主题模板startup修改

    shopify startup主题是一个很好的直接面向消费者DTC品牌的shopify模板,具有增强的推荐部分可定制性强,适合‎ ‎服装和配饰, 健康与美容, 家居与园艺‎,Dro…

    技术杂谈 2023年5月31日
    095
  • Wireshark基本介绍和学习TCP三次握手

    原文:http://www.cnblogs.com/tankxiao/archive/2012/10/10/2711777.html 阅读目录 wireshark介绍 wiresh…

    技术杂谈 2023年6月1日
    079
  • 让gitlab暴露node-exporter供外部prometheus使用

    花了两天部署了一套监控服务 prometheus+node-exporter+grafana,公司的gitlab服务器准备部署node-exporter的时候突然发现gitlab已…

    技术杂谈 2023年7月25日
    060
  • 《慢食、慢生活》笔记

    想定义”品质”,却只说”所谓有品质的产品必须是优质的”,这并没有解决品质的相对性问题。 所谓”优质”,是指一…

    技术杂谈 2023年5月31日
    088
  • GCC编译C:C++的四个过程

    从源码到可执行程序,经历四个过程: 预处理、编译、汇编和链接,前三步由使用编译器来完成、链接由链接来完成。 编译器将编译工作主要分为预处理,编译和汇编三部 连接器的工作是把各个独立…

    技术杂谈 2023年7月11日
    047
  • ToStringBuilder如何忽略null和空字符串

    日常开发中,经常用org.apache.commons.lang3.builder.ToStringBuilder工具类来记录日志,比如: 输出如下: 从结果上看: 行1:也就是O…

    技术杂谈 2023年5月31日
    089
  • 微信白名单获取

    公司在微信WIFI登录项目中,需要在BRAS设备上添加微信服务器的IP地址到白名单列表中,以实现用户连接热点后,能够使用微信中的功能(如:添加公众号),但不能使用其他应用进行上网操…

    技术杂谈 2023年7月11日
    074
  • 【邂逅Django】——(三)视图

    🛠️ Part 3:【邂逅Django】—— (三)视图 🛠️ Part 4:【邂逅Django】—— (四)表单和通用视图 🛠️ Part 5:【邂逅Django】—— (五)完…

    技术杂谈 2023年7月24日
    081
  • Kafka 概述

    kafka 是一个为事件流而生的分布式消息系统,广泛应用于网页用户记录跟踪,IOT 设备,日志采集,系统监控等场景。 kafka 是用于构建实时数据管道和流应用程序。具有横向扩展,…

    技术杂谈 2023年7月24日
    051
  • 分布式锁的三种实现方式

    一、zookeeper 1、实现原理: 基于zookeeper瞬时有序节点实现的分布式锁,其主要逻辑如下(该图来自于IBM网站)。大致思想即为:每个客户端对某个功能加锁时,在zoo…

    技术杂谈 2023年5月31日
    093
  • 自己动手写乞丐版线程池

    线程池的具体实现 在上一篇文章线程池的前世今生当中我们具体去介绍了线程池当中的原理。在线程池当中我们有很多个线程不断的从任务池(用户在使用线程池的时候不断的使用 execute方法…

    技术杂谈 2023年7月23日
    074
  • HashMap源码个人理解

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    技术杂谈 2023年7月11日
    053
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球