MySQL实战之深入浅出索引(上)

news2025/7/21 21:50:07

1.前言

提到数据库,大家肯定会想到数据库的索引,很多人都知道索引是为了提高查询效率的,那么今天我就给大家讲一下,什么是索引,索引的数据结构是什么,索引是如何工作的。

因为索引的内容比较多,会分为上下两篇进行讲解。

2.索引的常见模型

索引的出现是为了提高查询效率,但是实现索引的方式有很多种,所以这里就引入了索引模型的概念。可以用于提高读写效率的数据结构由很多,这里就先介绍三种比较常见、也比较简单的数据结构,分别是哈希表、有序数组和搜索树。

哈希表是一种以键值对存储数据的结构,我们只要输入带查找的键即key,就可以找到其对应的值即value。哈希的思路很简单,把值放到数组里面,用一个哈希函数把key换算成一个确定的位置,然后把value放在数组的这个位置。

不可避免的,多个key值经过哈希函数的换算,会出现同一个值的情况。处理这种情况的一种方法是拉出一个链表。

假设,你现在维护着一个身份证信息和姓名的表,需要根据身份证号查询对应的名字,这是对应的哈希索引的示意图如下所示
在这里插入图片描述
图中,User2和User4根据身份证号算出来的值都是N,但没关系,后面还跟了一个链表。假设,这时候你要查询ID_card_n2对应的名字是什么,处理步鄹就是:首先,将ID_card_n2通过哈希函数算出N;然后,按照顺序变量,找到User2.

需要注意的是,图中四个ID_card_n的值并不是递增的,这样做的好处是新增的User时速度会很快,只需要往后追加。但是缺点是,因为不是有序的,所以哈希索引做区间查询的速度是很慢的。

你可以设想一下,如果你现在要找身份证号在[ID_card_x, ID_card_y]这个区间的所有用户,就必须要全部扫描一遍。

所以,哈希表这种结构适用于只有等值查询的场景,比如Memcached一些NoSQL引擎。

而有序数组在等值查询和范围查询场景中的性能就很优秀。还是上面这个根据身份证号查询名字的例子,如果我们使用有序数组来实现的话,示意图如下所示:
在这里插入图片描述
这里我们加上身份证号没有重复,这个数组就是按照身份证号递增的顺序保证的。这时候如果你要查ID_card_n2对应的名字,用二分法就可以快速得到,这个时间复杂度是O(log(N)).

同时很显然,这个索引结构支持范围查询。你要查身份证号在[ID_card_x, ID_card_y]区间的User,可以先用二分法找到ID_card_X,然后向右遍历,查到第一个大于ID_card_y的身份证号,退出循环。

如果仅仅看查询效率,有序数组就是最好的数据结构了。但是,在需要更新数据的时候就麻烦了,你往中间插入一个记录就必须的挪动后面的所有记录,成本太高。

所以有序数组只适用于静态存储引擎

二叉搜索树也是大学教程里面经典的数据结构,还是上面根据身份证号查询名字的例子,如果我们用二叉搜索树实现的话,示意图如下:
在这里插入图片描述
二叉搜索树的特点是:父节点左子树所有节点的值小于父节点的值,右子树所有节点的值大于父节点的值。这样如果你要查ID_card_n2的话,按照图中的搜索顺序就是按照UserA->UserC->UserF->User2这个路径得到,这个时间复杂度是O(log(n)).

当然为了维持O(log(n))的查询复杂度,你就需要保持这棵树是平衡二叉树,为了做这个保证,更新的时间复杂度也是O(log(n))。

树可以有二叉,也可以有多叉。多叉树就是每个节点右多个儿子,儿子之间的大小保证从左到右递增。二叉树是搜索效率最高的,但实际上大多数数据库存储并不适用二叉树。其原因是,索引不止在内存中,还要写到磁盘上。

你可以想象一下一颗100万节点的平衡二叉树,树高20。一次查询可能要访问20个数据块。在机械硬盘时代,从磁盘随机读一个数据库需要10ms左右的寻址时间。也就是说,对于一个100万行的表,如果使用二叉树存储,单独访问一行可能需要20个10ms的时间,这查询就太慢了。

为了让一个查询尽量少的读磁盘,就必须让查询访问尽量少的数据库。那么,我们就不应该使用二叉树,而是要用N叉树,这里的N指的是数据库的大小。

以InnoDB的一个整数字段索引为例,这个N差不多是1200。这棵树高是4的时候,就可以存1200的3次方个值,这已经是17亿了。考虑到树根的数据库总是在内存中,一个10亿行的表上一个整数字段的索引,查找一个值最多只需要访问3次磁盘,其实,树的第二层也很大概率在内存中,那么访问磁盘的平均次数就更少了

N叉树由于读写上的性能优点,以及适配磁盘的访问模式,已经被广泛应用到数据库索引引擎中了。

不管是哈希表还是有序数组,或者N叉树,他们都是不断迭代、不断优化的产物和解决方案。数据库技术发展到今天,跳表、LSM树等数据结构也被用于引擎设计中了。

你要心里有个概念,数据库底层存储的核心就是基于这些数据模型的。没碰一个新数据库,我们需要先关注他的数据模型,这样才能从理论上分析出这个数据库的使用场景。

接下来我们就来分析一下mysql 中InnoDB存储引擎的索引模型。

3.InnoDB的索引模型

在InnoDB中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称之为索引组织表。又因为我们前面提到的,InnoDB使用了B+树索引模型,所以数据都是存储在B+树中的。

每个索引在InnoDB里面对应一颗B+树。

假设,我们有一个主键列为ID的表,表中的字段k,并且k上有索引。

mysql> create table T(
id int primary key, 
k int not null, 
name varchar(16),
index (k))engine=InnoDB;

表中R1-R5的(ID,k)值分别为(100,1),(200,2),(300,3),(500,5),(600,6),两颗索引树如下图
在这里插入图片描述

从图中不难看出,根据叶子节点的内容,索引类型分为主键索引和非主键索引。

主键索引的叶子节点存储的是整行数据。在InnoDB里,主键索引也称为聚簇索引。

非主键索引的叶子节点存储的是主键索引的值,在InnoDB里,非主键索引也称为二级索引。

根据上面的索引结构说明,我们来讨论一个问题,基于主键索引和普通索引的查询有什么区别?

  • 如果语句是select * fromT where ID=500,即主键查询方式,则只需要搜索ID这颗B+树
  • 如果语句是 select * from T where k = 5,即普通索引查询方式,则需要先搜索k索引树,得到ID为500,在通过ID索引树搜索一次,这个过程叫做回表。

也就是说,基于非主键索引的查询需要多扫描一颗索引树,因此,我们在应用中应该尽量使用主键查询。

4.索引维护

B+树为了维护索引的有序性,在插入新值的时候需要做必要的维护。以上面的这个图为例,如果要插入新的行ID值为700,则只需要在R5的记录后面插入一个新纪录。如果新插入的ID值为400,就比较麻烦了,需要逻辑上挪动后面的数据,空出位置。

而更糟的情况是,如果R5所在的数据页已经满了,根据B+树的算法,这时候要申请一个新的数据页,然后挪动部分数据过去。这个过程称为页分裂。在这种情况下,性能就会收到影响。

除了性能外,页分裂操作还影响数据页的利用率。原本放在一个页的数据,现在分到两个页中,整体空间利用率大约降低50%。

当然有分裂就有合并。当相邻两个页由于删除数据,利用率很低之后,会将数据页做合并。合并的过程,可以认为是分裂的逆过程。

基于上面的索引维护过程说明,我们来讨论一个案例:
那可能在一些建表规范里面见到类似的描述,要求建表语句里一定要有自增主键。当然事无绝对,我们来分析一下哪些场景应该使用自增主键,哪些场景不应该。

自增主键指的是自增列上定义的主键,在建表语句中一般这样定义:NOT NULL PRIMARY KEY AUTO_INCREMENT.

插入新纪录的时候可以不指定ID的值,系统会获取当前ID最大值加1作为下一条记录的ID值。

也就是说,自增主键的插入数据模式,正符合了我们前面提到的递增插入场景。每次插入一条新纪录,都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂。

而有业务逻辑的字段做主键,则往往不容易保证有序插入,这样写数据成本相对较高。

除了考虑性能外,我们还可以从存储空间的角度来看。假设你的表中确实有一个唯一的字段,比如字符串类型的身份中号,那应该用身份证号做主键,还是用自增字段做主键呢?

由于每个非主键索引的叶子节点上都是主键索引的值。如果用身份证号做主键,那么每个二级索引的叶子节点占用约20个字节,而如果用整形做主键,则只要4个字节,如果是长整型则是8个字节。

显然,主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小。

所以,从性能和存储空间的方面考量,自增主键往往是更合理的选择。

5.小结

今天,我跟你分析了数据库引擎可用的数据结构,介绍了 InnoDB 采用的 B+ 树结构,以及为什么 InnoDB 要这么选择。B+ 树能够很好地配合磁盘的读写特性,减少单次查询的磁盘访问次数。

由于 InnoDB 是索引组织表,一般情况下我会建议你创建一个自增主键,这样非主键索引占用的空间最小。但事无绝对,我也跟你讨论了使用业务逻辑字段做主键的应用场景。

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

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

相关文章

大数据应用要经得起考验,不可盲目跟风_光点科技

一项大数据应用,如果不是经得起推敲的,那就值得怀疑它是不是优秀的大数据应用,是不是有可利用的价值,是不是值得将人力物力财力花费在其中。所以,必须对大数据应用进行必要的筛选,做一定的检验之后才可以做…

vegfr2药物|适应症|市场销售数据-上市药品前景分析

癌症作为人类身体健康的主要威胁,其高死亡率一直是人类死亡的主要原因。尽管人类为控制癌症付出了巨大的努力,然而癌症的发病率和死亡率还是在高速增长。而肺癌、结直肠癌、肝癌和乳腺癌等被认为是癌症死亡的主要因素。而根据科研人员发现,癌…

JavaScript HTML DOM 简介

文章目录JavaScript HTML DOM 简介HTML DOM (文档对象模型)HTML DOM 树查找 HTML 元素通过 id 查找 HTML 元素通过标签名查找 HTML 元素通过类名找到 HTML 元素下面我们将学到如下内容JavaScript HTML DOM 简介 通过 HTML DOM,可访问 JavaScript HTML 文档的所有元素…

高清无码的MP4如何采集?python带你保存~

前言 大家早好、午好、晚好吖 ❤ ~ 又是我,我又来采集小姐姐啦~ 这次我们采集的网站是(看下图): 本文所有模块\环境\源码\教程皆可点击文章下方名片获取此处跳转 话不多少,我们赶快开始吧~ 第三方模块: requests >>> pip install requests 如果安装python第三方模块…

刷题专练之数组移除元素

文章目录前言一、移除元素1.题目介绍2.思路:3.代码二、移动零1.题目介绍2.思路3.代码三、删除有序数组中的重复项1.题目介绍2.思想3.代码四、80. 删除有序数组中的重复项 II1.题目介绍2.思路3.代码4.推荐题解前言 我每个刷题篇的题目顺序都是特别安排的,…

FSM——squirrel状态机使用

FSM——squirrel状态机使用 1 FSM介绍 1.1 概念 FSM(finite state machine):有限状态机 是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。核心内容:有限个状态、通过外部操作引起状态的转移。用来对状态的流转进行解耦&a…

C++031-C++日期模拟

文章目录C031-C日期模拟日期模拟题目描述 给出天数求月份日期题目描述 给出天数求月份日期-倒计时题目描述 求任意日期插值在线练习:总结C031-C日期模拟 在线练习: http://noi.openjudge.cn/ https://www.luogu.com.cn/ 日期模拟 题目描述 给出天数求月…

CSS 网页动画【快速掌握知识点】

目录 前言 一、使用CSS3动画 二、使用CSS过渡 三、使用CSS变换: 前言 CSS是一种用于网页设计和排版的语言,也可以用它来制作网页动画。 一、使用CSS3动画 CSS3引入了动画属性,允许您为元素设置动画效果。您可以使用关键帧来定义动画的开始…

MVP简单模型搭建【架构】

MVP简介 MVP是一种项目架构设计模式(说白了就是我们产品的一种设计方案) 其实MVP本质 就是将View和Model完全隔离,通过Presenter统一调度管理(Presenter扮演着中介的角色)传统的设计思路是我们直接跟房东谈&#xff0…

聊聊动态线程池的9个场景(改进版)

线程池是一种基于 池化思想管理线程 的工具,使用线程池可以减少 创建销毁线程的开销,避免线程过多导致 系统资源耗尽。在 高并发以及大批量 的任务处理场景,线程池的使用是必不可少的。 如果有在项目中实际使用线程池,相信你可能…

数仓模型之维度建模

目录 1、数仓架构原则 2、如何搭建一个好的数仓 2.1 建模方法 2.2 建模解决的痛点 2.3 数仓系统满足的特性 2.4 数仓架构设计 3、维度建模 4、案例 5、问题讨论 今天我们来聊聊在数仓模型中举足轻重的维度建模。 简单而言,数据仓库的核心目标是为展现层提…

Hive学习——开窗函数精讲

目录 一、基于行的窗口函数——行的起点~行的终点 二、基于值的窗口函数——值的起点~值的终点 三、基于分区的窗口函数 四、基于缺省的窗口函数 五、lead与lag 六、first_value和last_value 七、排名函数——rank(113)、dense_rank(112)、row_number(123) 八、NTILE分…

nvm (node版本管理工具)安装的详细步骤,并解决安装过程中遇到的问题

1、下载NVM,跳转下载链接后,如下图,下载红框后解压文件 2、安装 注意:双击安装之后,会有两个地址选择, 1、地址中不能存在空格 2、不要放在C盘中,后面需要改个设置文件,安装到C盘的…

银行家算法

银行家算法 银行家算法是一种用来避免操作系统死锁出现的有效算法,所以在引入银行家算法的解释之前,有必要简单介绍一下死锁的概念。 一、死锁 死锁:是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成…

Gitlab Linux 环境安装

环境检查是否已经安装了gitlab,安装了需要卸载# 检查当前环境是否已经安装了gitlab,并且查看版本 gitlab-rails console # 删除命令 yum remove git# 查找命令 rpm -qa | grep gitlab # 删除命令 rpm -e gitlab-ce-12.10.1-ce.0.el8.x86_64 # 查找命令f…

DS期末复习卷(八)

一、选择题(30分) 1.字符串的长度是指( C )。 (A) 串中不同字符的个数 (B) 串中不同字母的个数 (C ) 串中所含字符的个数 (D) 串中不同数字的个数 2.建立一个长度为n的有序单链表的时间复杂度为( C ) (A) O(n) (B) O(1) © …

小米/红米手机数据恢复:从小米手机恢复已删除的数据

如果您不小心删除了小米手机上的数据,后来发现您需要它,那么本文适合您。我将向您介绍一些最可靠的小米恢复方法,以将您的数据恢复到您的设备上。无论您是否有备份,都可以处理。让我们开始吧! 小米数据恢复 - 如何做&a…

一篇学习JVM

基础入门 1.JDK、JRE、JVM三者间的联系与区别 JDK JDK(Java SE Development Kit),Java标准开发包,它提供了编译、运行Java程序所需的各种工具和资源,包括Java编译器、Java运行时环境,以及常用的Java类库等。 下图是JDK的安装目…

博客管理系统--项目说明

项目体验地址(账号:123,密码:123)http://120.53.20.213:8080/blog_system/login.html项目码云Gitee地址:https://gitee.com/GoodManSS/project/tree/master/blog_system(一)准备工作…

【ArcGIS Pro二次开发】(7):地图(Map)的基本操作

地图是ArcGIS Pro中的基础起点,也是大多数工程的基础。主要用于显示表示空间数据的图层。 一、地图(Map)的基本操作示例 1、获取当前地图 var map MapView.Active.Map; 2、获取一级图层 var lys map.Layers; 用于获取地图中的单一图层,以及图层组…