数据结构:Mysql索引原理(通俗易懂)

news2024/11/26 11:52:06

目录

  • 前言
  • 正文
    • 索引结构-数组
      • 无序数组
      • 有序数组
    • 索引数据结构-Hash
      • Hash冲突-链式寻址法
      • Hash冲突-再哈希法
      • Hash冲突-开放地址法
    • 索引数据结构-树
      • 二叉树
      • 平衡二叉树
      • 红黑树
        • 红黑树的特性
        • 红黑树如何减少旋转
      • B树
      • B+树
    • Mysql的索引
      • 一级索引
      • 二级索引
  • 总结

前言

在工作中如果经常写业务代码,说白了就是CURD,比如做一个查询功能,我们会将前端参数传递到后端后拼接SQL查询数据,那为了提高用户体验,查询速度肯定是越快越好,这就要求我们能够对SQL调优,让查询速度更快。

由于数据需要持久化,且数据量庞大,所以只能将数据存储到硬盘中,我们在查询时需要先将数据从硬盘中加载到内存中,因为CPU从内存中拿数据的效率要比从硬盘中拿数据的效率高很多。查询数据时通常需要拿到当前的数据后对比是否满足查询条件,这个过程由于是在内存中进行的所以速度非常快,而要进行数据对比时,就需要先将磁盘中的数据加载到内存中,这就是我们平常所说的IO,IO的时间损耗比较高,基本上整个查询过程的IO的占比要比数据对比耗时高得多,所以就要想办法减少IO的次数,提高查询效率。

在这里插入图片描述

假设每次加载数据大小是16K,而表数据中每行大小为1K,也就是1次IO只能加载16条数据,那对于量大的表,完全没法玩了。那可以为每行数据建立一个唯一的ID列,然后在外部建立一个key-value的容器,key为ID列的值,value为对应数据行的数据地址,这样每个key-value的数据大小肯定远远小于每行数据的大小,这样我一次IO就能加载很多key-value到内存中,然后去做比较,找出符合条件的key-value,然后通过value存储的地址去加载表中的数据,这样效率就可以大大提高,索引就是这种原理。

正文

既然知道可以通过索引来提高查询效率了,那如何设计索引呢?

索引结构-数组

数据无疑是最简单的结构了,初始化Entry<key,value>数组,当插入数据后,往数组中维护key为行数据的所有列值,value为行数据的空间地址;

在这里插入图片描述

无序数组

如果数组中的数据不是根据key值进行排序插入的,那么每次插入只需要往容量值大小位置插入即可,效率较高;当删除数据时,如果不考虑空间碎片化问题时,即允许被删除位置为null,删除的效率也是比较高;虽然插入删除效率较高,但是查询效率却非常低,由于是无序的,意味着需要从头遍历数组,直到找出合适的数据位置,时间复杂度为O(N),那如果有1百万个索引值,这样的效率显然不能满足我们的要求;无序对范围查询不友好,需要遍历整个数组;
在这里插入图片描述

有序数组

无序的数据查询的时间复杂度为O(N),不能满足大数据量下的要求,那如果数据按key的顺序进行插入,变成一个有序数组,有序数组查询时可以使用二分查找法,时间复杂度为O(logN),这样的查询效率就比较高了,由于是有序的,对于范围查询效率比较高,不需要遍历整个数组。但有序数组在插入数据时,会涉及数组的移动,在大数据量的场景下,插入的数据位置刚好比较靠前,那么后面的数据得往后挪动,那数据插入这块耗时就比较久,显然也不适合作为索引的数据结构。
在这里插入图片描述

数组由于需要先开辟空间,因此要指定长度,当数据空间满时,还需要考虑扩容问题,显然大数据场景下不适合使用数据作为数据结构;

索引数据结构-Hash

数组插入时涉及到空间的移动,插入效率低下,那如果对key进行hash后跟数组长度取模,这样就可以计算出key在数组中的下标,而且查询的时间复杂度为O(1),听起来增删改查都好快,那选Hash来作为索引的数据结构吧。

在这里插入图片描述

别高兴的太早,再好的hash算法都可能发生冲突,也就是不同的key计算出来的下标地址可能一样。

解决Hash冲突有多种方式,我们来分析下有没有适合的;

Hash冲突-链式寻址法

在每个索引节点新增一个字段next,用来保存下个索引节点的地址;当计算出数组已经存在数据时,开辟新的空间存储插入节点,然后将空间地址赋值给数组对应链表的尾节点的next字段;通过这种方法解决hash冲突后,查询时通过公式计算出数组下标,拿出当前数组下标节点(头节点),将链表挨个遍历对比key的值是否一致,查询值符合要求的数据。这种结构当数据量大时,可能hash冲突非常严重,导致链表长度非常长,这样查询遍历的损耗时间就很长。

数据量大时,由于链表较长,每次IO只会加载一定量的数据到内存中,量大IO频率变高,导致时间较长。HashMap在1.8引入了红黑树,就是用于优化链表过长查询效率低下问题。

像HashMap、Redis采用这种数据结构方式,我理解是因为数据是存在内存中的,不像数据库的索引文件存在硬盘,所以节省了从硬盘读取数据的IO开销。

在这里插入图片描述

Hash冲突-再哈希法

当通过hash公式计算出下标,发生hash冲突时,使用新的hash公式或者原公式对hash后的值再进行hash,直到计算出来的下标位置为空时进行存储。那取值时,通过公式计算出下标后,比较key是否一致,不一样时再进行hash计算下标进行比值,以此类推直到查询到新的值为止;再Hash法可能需要多次公式计算,所以比较消耗时间。

在这里插入图片描述

Hash冲突-开放地址法

当发生hash冲突时,在当前下标往后进行遍历查找,寻找出地址为空的下标进行存储。当查询时,通过hash公式计算出下标,然后比较Key值是否一致,不一致则往后遍历寻找,如果遍历的位置下标为空时,查询结束,代表当前数组中不存在符合条件的数据了。由于遇到空的就会结束查找,所以当删除数据时,不能将该下标数据清空(只能弄个字段来做标记),不然其后面的同义数据(Hash冲突的哪些节点数据)将会查询不到。那为了减少hash冲突,就要求填充因子要尽量小(当前已存储容量/容量总长度),数据量大时,在保持填充因子小的情况会空间利用率会变得很低。

ThreadLocal内部数据结构就是采用开放地址法来解决Hash冲突的。

在这里插入图片描述

Hash方式不适合进行范围查找,只能扫描整个数据容器把符合条件的筛选出来,对于没有范围查找的索引key,可以使用hash的方法来作为底层数据结构;

索引数据结构-树

既然hash不支持索引,那我们可以考虑使用Tree来作为数据结构

二叉树

使用二叉树来作为索引的数据结构,插入时比较key的大小,比当前节点的key大时往右边插入,比当前节点的key小时往左边插入。查询时也是通过比较其key值看是否相等,不相等则比较大小来解决查询方向,使用二叉树的查询效率比较快,时间复杂度为O(logn),查询效率高且支持范围查询;

在这里插入图片描述

二叉树虽然查询效率高,且支持范围查询,但是在极端的情况下会链化,如按顺序插入10、20、30、40、50,这种情况的查询时间复杂度就变成O(N)了,大数据量的情况下显示不能被接受;
在这里插入图片描述

平衡二叉树

为了解决二叉树链化的情况,为此增加了一条限制,左右两个子树的高度差不能超过 1,超过时通过左旋和右旋来维持平衡,因此左右两边相对平衡,因此称之为平衡树;

在线演示地址:https://www.cs.usfca.edu/~galles/visualization/AVLtree.html

插入30后,由于10的右边与左边的高度差为2,因此需要进行左旋

在这里插入图片描述

插入10后,由于30的左边与右边的高度差为2,因此需要进行右旋

在这里插入图片描述

平衡二叉树通过旋转避免了极端情况下链化的场景,但是在大数据量的情况下,每次插入可能因为维持平衡而导致多次左旋或右旋,这样在插入效率上极低,显然平衡而插入不适合大数量的场景;

红黑树

平衡二叉树数据量大的插入数据由于要通过多次的左旋或者右旋保持平衡,导致效率低下,那有没有办法可以减少左旋或者右旋的次数呢?

红黑树可以认为是平衡二叉树的增强版,它引入了颜色标记来减少旋转的次数,之前笔者有专门写了一篇红黑树的,想深入了解红黑树可以去看《HashMap源码学习:红黑树原理详解》

红黑树的特性

红黑树在线演示地址:https://www.cs.usfca.edu/~galles/visualization/RedBlack.html

  • 根节点必须是黑色节点。
  • 节点是红色或黑色。
  • 所有叶子都是黑色。(叶子是Null节点)
  • 每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
  • 从任一结点到其每个叶子的所有路径都包含相同数目的黑色节点。

红黑树如何减少旋转

红黑树左边结构要保持平衡只需要变色即可;

在这里插入图片描述

平衡二叉树左边要保持平衡需要以35为旋转节点进行左旋,再以50为旋转节点进行右旋,所以需要经过两次旋转;

在这里插入图片描述

红黑树的变色只需要修改字段的状态值即可,不涉及多个指针变动,减少了旋转,所以插入效率要比平衡二叉树高;

好了,既然红黑树的插入效率提高了很多,那么我们来看下查询效率吧,在大数据量的情况,由于每个节点只有两个分支,树的高度会变得非常多层,如果每次从磁盘中加载了一层,总共30层高的话,需要进行30次IO,那查询会损耗大量时间在IO上,显然这种不适合作为大数据量下的索引数据结构;

B树

红黑树在大数据量的情况下缺点比较明显,说白了就是分支偏少,每次查询的范围较大,如果分支增加那么就可以缩小范围,树的高度也会因此而减低。多路树就是在让每个节点拥有的分支更多,让查询的范围缩小;

B树在线演示地址:https://www.cs.usfca.edu/~galles/visualization/BTree.html

在这里插入图片描述

B树也是多路树的一种实现,B-树中的每个节点除了存放索引值、其它节点引用地址外,还存储数据。B树每个节点存储子节点个数取决于每个子节点的大小,比如每次加载一个16K的节点,每个子节点占用4k(索引值、数据、引用地址等),即每个节点只能放4个子节点。

在查询时,如果子节点存储数据过大,会导致IO次数变多,比如每次加载一个页16K到内存中,每个节点占用2K,最终只能加载8个节点到内存中,节点的占用越低每次加载到内存的节点树就会越多,在内存中进行计算肯定是比加载数据IO时间少得多的,所以像Mysql每行数据字段特别多的情况下,使用B树显示查询效率会很慢。

B+树

B树的缺点是会因为节点文件过大导致读取磁盘次数变得,让查询效率变低。那如果节点不存储真实数据,那其大小就会变得很低,B+树就是结合这种场景,将数据放到叶子节点中,而我们的非叶子节点只存索引值、其它节点引用值即可,这样可以大大减少读取磁盘的次数了。叶子节点通过指针链化,这种结构支持范围查询;B+树在大数据量的场景下,查询效率也是比较高,这也是为什么Mysql使用B+树的原因了。

B+树在线演示地址:https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html

在这里插入图片描述

Mysql的索引

一级索引

Mysql的InnoDB存储引擎会以主键作为聚簇索引,如果没有主键则以非空的唯一索引作为聚簇索引,如果都没有则以隐式的方式创建自增的ID来作为聚簇索引。聚簇索引就是一级索引,它以B+树作为数据结构,存储了索引值和真实的行数据值。

现有表数据(ID为主键):
在这里插入图片描述

一级索引建立后:

在这里插入图片描述

当我们使用ID作为搜索条件时,就会走索引,快速拿到查询数据

select * from student stu where stu.id=2; 

二级索引

二级索引不会存储完整的行数据,只会存储索引值以及一级索引的值;

下面以年龄字段建立普通索引:

在这里插入图片描述

当执行查询语句时,会根据age索引找到一级索引的值,由于要返回整行的数据,二级索引查询到值后,拿出一级索引值回表查询,也就是去一级索引树中查询整行数据。那如果刚好要返回的数据值在二级索引树中存在,就不要回表了。

select * from student stu where stu.age=12; 

总结

不同的数据结构适用于不同的场景,具体的结构选型应根据数据量、字段类型、查询频率来决定。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/701226.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

信号链噪声分析4

目录 概要 整体架构流程 技术名词解释 技术细节 5.计算信号链的噪声 噪声谱密度 重点注意事项 有源滤波器配置 小结 概要 提示&#xff1a;这里可以添加技术概要 本文介绍对高速宽带宽信号链进行噪声性能理论分析的各个步骤。尽管选择了一个特 定信号链&#xff0c;…

传统商超巨头折戟即时零售

今年的618&#xff0c;包含沃尔玛、永辉、大润发、华润、物美、麦德龙、盒马、联华、家家悦、中百、华联在内10大商超巨头都集体“失声”了。 值得注意的是&#xff0c;今年618不仅各大商超没有公布相关数据&#xff0c;且在所有即时零售平台中&#xff0c;仅有商超到家的合作…

怎么用postman进行自动化接口测试,终于学到了

​ 目录 背景描述 创建一个GET请求 在pre-request scripts构建签名 脚本写在环境变量中 postman console的用法 Collection Runner 自动化API测试 创建接口的测试用例 选择并运行自动化接口测试 测试结果 总结&#xff1a; 背景描述 有一个项目要使用postman进行接…

元宇宙虚拟人物3d建模贯穿产业链数字化转型全链条

随着5G、AI、VR/AR、区块链、云计算、脑机等技术的发展&#xff0c;给构建这个虚拟的宇宙提供了技术支撑。今年以来&#xff0c;因为疫情等原因&#xff0c;大众在虚拟空间停留的时间变得更长&#xff0c;元宇宙概念重新火热。 元宇宙虚拟人的基本特征包括&#xff1a; 沉浸式体…

Android hook、检测及对抗相关

frida——hook 内存访问断点 环境&#xff1a;app&#xff1a;arm64 python 3.10 frida 15.2.2 简单的内存访问断点代码&#xff0c;可能还有些bug&#xff0c;根据apk需要自己改&#xff0c;下文为在apk中指定的地址调用函数时内存断点才被激活&#xff0c;以下需要…

【GESP】2023年03月图形化二级 -- 快乐时光

文章目录 快乐时光1. 准备工作2. 功能实现3. 设计思路与实现&#xff08;1&#xff09;角色、舞台背景设置a. 角色设置b. 舞台背景设置 &#xff08;2&#xff09;脚本编写a. 角色&#xff1a;小猫b. 角色&#xff1a;小猴 4. 评分标准 快乐时光 1. 准备工作 &#xff08;1&am…

CSS知识点汇总(十)--移动端适配

文章目录 怎么做移动端的样式适配&#xff1f;1、方案选择2. iPhoneX 适配方案 怎么做移动端的样式适配&#xff1f; 在移动端虽然整体来说大部分浏览器内核都是 webkit&#xff0c;而且大部分都支持 css3 的所有语法。但手机屏幕尺寸不一样&#xff0c;分辨率不一样&#xff0…

jenkins共享库配置及设计

jenkins共享库做模块封装时遇到的问题总结&#xff1a; 背景描述:使用jenkins共享库对SCM subversion操作进行封装时&#xff0c;使用了Checkout插件&#xff0c;生成的检出脚本代码为 checkout([$class: SubversionSCM, additionalCredentials: [], excludedCommitMessages: …

【Dashy安装使用】本地Linux 部署 Dashy 并远程访问

文章目录 简介1. 安装Dashy2. 安装cpolar3.配置公网访问地址4. 固定域名访问 转载自cpolar极点云文章&#xff1a;本地Linux 部署 Dashy 并远程访问 简介 Dashy 是一个开源的自托管的导航页配置服务&#xff0c;具有易于使用的可视化编辑器、状态检查、小工具和主题等功能。你…

spring源码编译笔记

下载源码 地址https://github.com/spring-projects/spring-framework/tree/v5.2.9.RELEASE 查看gradle对应版本 spring-framework-5.2.9.RELEASE/gradle/wrapper/gradle-wrapper.properties # 其他配置暂未了解具体用处&#xff0c;一切默认值 distributionBaseGRADLE_USE…

vue3案例

代码 <template><div class"app-page"><divv-for"title in data.titleArr":key"title.id"class"title-box"click"onClick(title)"><span>{{ title.name }}</span><img v-if"title…

Java基础——正则表达式

1 概述 正则表达式用于匹配规定格式的字符串。 除了上面的以外&#xff0c;还有一个符号就是括号&#xff0c;括号括起来的表示一个捕获组&#xff0c;一个捕获组可以作为一个重复单位来处理。 2 使用 2.1 判断是否匹配 String自带了一个可以使用正则表达式判断字符串是…

卸载 Navicat:正版 MySQL客户端,真香!

最近看到一款数据库客户端工具&#xff0c;DataGrip&#xff0c;是大名鼎鼎的JetBrains公司出品的&#xff0c;就是那个出品Intellij IDEA的公司。DataGrip是一款数据库管理客户端工具&#xff0c;方便连接到数据库服务器&#xff0c;执行sql、创建表、创建索引以及导出数据等。…

【Python从入门到进阶】25、urllib获取快餐网站店铺数据

接上篇《24、urllib获取网站电影排行》 上一篇我们讲解了如何使用urllib的get请求抓取某某电影排行榜信息。本篇我们来讲解如何使用urllib的post请求抓取某某快餐网站店铺数据。 一、某某快餐网站介绍 1、某某快餐网站 某某快餐店网址为&#xff1a;http://www.kfc.com.cn/k…

SciencePub学术 | 国人友好类重点SCIEEI征稿中

SciencePub学术 刊源推荐&#xff1a;国人友好类重点SCIE&EI征稿中&#xff01;信息如下&#xff0c;录满为止&#xff1a; 一、期刊概况&#xff1a; 二、期刊要求 1. 论文为原创&#xff0c;未公开发表&#xff0c;初稿可提交中文版本&#xff0c;终稿必须是英文版本; …

Redis 跳表skiplist

跳跃表 在单链表中查询一个元素的时间复杂度为O(n)&#xff0c;即使该单链表是有序的&#xff0c;我们也不能通过2分的方式缩减时间复杂度。 跳跃表(skiplist)是一种有序数据结构&#xff0c;它通过在每个节点中维持多个指向其他节点的指针(注&#xff1a;可以理解为维护了多条…

【计算机网络】数据链路层之随机接入-CSMA/CA协议(无线局域网)

1.概念 2.无线局域网可否实现碰撞检验CD 3.方案 CSMA/CA 碰撞避免 4. 两种帧间间隔 IFS 为什么需要等待DIFS? 为什么需要等待SIFS? 为什么还要退避一段时间才能使用信道&#xff1f; 5.退避算法 使用退避算法的情况 退避算法 举例 6.信道预约 7.虚拟载波监听 8.题目 9.解析 …

分享一个上传按钮

先看效果&#xff1a; 再看代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>上传</title><link rel"stylesheet" href"https://fonts.googleapis.com/css2…

面对Android开发高薪诱惑,如何拿到大厂offer?

前言 出来打工最看重的就是薪资&#xff0c;作为一名5年开发经验的Android人员。在如今的大环境中薪资一降再降&#xff0c;还没人要。技术还还算可以但是面试一直被刷下来&#xff1f;这是为什么&#xff0c;本篇我们来聊聊Android开发面试中的关键。 面试过程 通常面试过程…

【Vue3】学习笔记-watch函数

与Vue2.x中watch配置功能一致 两个小“坑”&#xff1a; 监视reactive定义的响应式数据时&#xff1a;oldValue无法正确获取、强制开启了深度监视&#xff08;deep配置失效&#xff09;。监视reactive定义的响应式数据中某个属性时&#xff1a;deep配置有效。 <template&…