MySQL的索引和B+tree结构

目录

0.关于索引的常见面试题

1.什么是索引?

索引的优缺点

 2.索引的数据结构,为什么InnoDb引擎使用B+tree作为索引的数据结构?

分析怎样的索引才是好的

二插搜索树

红黑树

 B-Tree

B+Tree

哈希

为什么 InnoDB 存储引擎选择使用 B+tree 索引结构?

3.B+tree结构的数据个数和树高的计算

4.索引的分类

 5.有关索引的语法

6.索引的使用

7.索引失效的情况

8.MySQL中自动或人为优化索引的方法

覆盖索引优化

前缀索引优化

索引下推优化


0.关于索引的常见面试题

  • 什么是索引?
  • 索引底层使用了什么数据结构和算法?
  • 为什么 MySQL InnoDB 选择 B+tree 作为索引的数据结构?
  • 什么情况下索引会失效?
  • 有什么优化索引的方法?
  • .....

1.什么是索引?

一句话简单来说,索引的出现就是为了提高数据查询的效率,就像书的目录一样。一本 1000 页的书,如果你想快速找到其中的某一个知识点,在不借助目录的情况下,那你可能得找一会儿。同样,对于数据库的表而言,索引其实就是它的“目录”。 

所以,索引就是提高查询速度的数据结构(有序的)。而能提高查询速度的,说明这些数据是按照一定规则排序好的。

索引的优缺点

优点:

  • 提高数据检索效率,降低数据库的IO成本
  • 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗

缺点:

  • 索引列也是要占用空间的
  • 索引大大提高了查询效率,但降低了更新的速度,比如 INSERT、UPDATE、DELETE(插入等这些操作需要把数据进行排序)

在 MySQL 中,索引是在存储引擎层实现的,所以并没有统一的索引标准,即不同存储引擎的索引的工作方式并不一样。

2.索引的数据结构,为什么InnoDb引擎使用B+tree作为索引的数据结构?

前面说了索引是数据结构,那数据结构是有多种的。为什么在大多数情况下,B+tree打败了其他数据结构呢?

分析怎样的索引才是好的

数据库的索引是保存到磁盘上的,因此当我们通过索引查找某行数据的时候,就需要先从磁盘读取索引到内存,再通过索引从磁盘中找到某行数据,然后读入到内存,也就是说查询过程中会发生多次磁盘 I/O,而磁盘 I/O 次数越多,所消耗的时间也就越大。

所以,我们希望索引的数据结构能在尽可能少的磁盘的 I/O 操作中完成查询工作,因为磁盘 I/O 操作越少,所消耗的时间也就越小。

另外,MySQL 是支持范围查找的。

所以,要设计一个适合 MySQL 索引的数据结构,至少满足以下要求:

  • 能在尽可能少的磁盘的 I/O 操作中完成查询工作;
  • 要能高效地查询某一个记录,也要能高效地执行范围查找(即索引最好是按照顺序排序的);

 不熟悉MySQL的同学想到数据结构,可能会想到数组,链表这些。这些是不适合在MySQL查询使用的。因为其查询效率低,时间复杂度是O(n)。而且使用数组插入又想要排序好的话,时间复杂度也是O(n)。

要想插入和查询性能都比较好的话,那可以想到二叉树,而有序排序的话,那就是二叉查找树。

二插搜索树

  • 顺序插入时候会形成一个单项链表,查询性能大大降低
  • 而在大数据量情况下,会导致层级较深,检索速度慢 (一层就是一次磁盘 I/O 操作)

 所以放弃二叉搜索树。

红黑树

红黑树是自平衡搜索树,是二插搜索树的其中一种类型。

  • 解决二叉树的顺序插入后,树不平衡的问题(但插入性能比二插搜索树差点)
  • 在大数据量的情况下,层级还是较深,检索速度较慢

B-Tree

现在就是要解决层次深的问题,很明显是每个节点只有两个子节点造成的,要是一个节点下有多个子节点,那层次就可以控制在合理范围了。可以用到B-Tree(多路平衡查找树) 。

这里,以一棵最大度数(max-degree,指一个节点的子节点个数)为5(5阶)的 b-tree 为例(每个节点最多存储4个key,5个指针)。

需要注意: B 树的每个节点都包含数据(索引+记录)

这里可以稍微解决红黑树层次深的问题,但还是不够好。数据都是以页为单位存放的, MySQL中一页是16k,而在B树中,非叶子节点和叶子结点都会存放数据,一页中可以放的指针和数据太少。当数据量也很大的时候,即层次可能还是比较深,IO次数变多

那可以想到只有叶子节点存放数据,而其他节点没有存放数据的,那一页中就可以保存更多的索引值了,那就可以使用到B+tree。

B+Tree

图片中橙色的是页/块,存储索引和数据。

上图是MySQL优化后的B+ Tree,和原始的相比是在叶子节点添加了双向循环链表的。

和B-tree相比:

  • 所有的数据都会出现在叶子节点。
  • 叶子节点形成一个双向循环链表。
  • 非叶子节点仅仅起到索引数据作用,具体的数据都是在叶子节点存放的。

 这样每页的索引可以放更多了,那树高自然就可以矮了嘛,磁盘的随机 I/O 读取次数相对就减少了。而且叶子结点之间用链表有序连接,所以扫描全部数据只需扫描一遍叶子结点,利于扫库和范围查询。

哈希

哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。
如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突(也称为hash碰撞),可以通过链表来解决。

其特点:

  • 只能用于等值查询较(=,in),不支持范围查询(between,>,< ,…)
  • 无法利用索引完成排序操作
  • 查询效率高,通常(不存在hash冲突的情况)只需要一次检索就可以了,效率通常要高于B+tree索引

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

为什么 InnoDB 存储引擎选择使用 B+tree 索引结构?

对比二叉搜索树和自平衡二叉树(以红黑树举例)

  • 层级更少,搜索效率高。

对比B-tree

  • B+ 树叶子结点之间用链表有序连接,所以扫描全部数据只需扫描一遍叶子结点,利于扫库和范围查询;B 树由于非叶子结点也存数据,所以只能通过中序遍历按序来扫。即是对于范围查询和有序遍历而言,B+ 树的效率更高
  • B+ 树更相比 B 树减少了 I/O 读写的次数。B+ 树的非叶子结点只存关键字不存数据,因而单个页可以存储更多的关键字,即一次性读入内存的需要查找的关键字也就越多,磁盘的随机 I/O 读取次数相对就减少了。
  • B+树的查询效率更加稳定,任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

对比Hash索引

  • B+tree支持范围匹配及排序操作

3.B+tree结构的数据个数和树高的计算

假设一行数据大小为1k,一页(一页大小是16k)中可以存储16行这样的数据。InnoDB 的指针占用6个字节的空间,主键假设为bigint,占用字节数为8。
可得公式:n * 8 + (n + 1) * 6 = 16 * 1024,其中 8 表示 bigint 占用的字节数,n 表示当前节点存储的key的数量,(n + 1) 表示指针数量(比key多一个)。算出n约为1170。

如果树的高度为2,那么他能存储的数据量大概为:1171 * 16 = 18736;
如果树的高度为3,那么他能存储的数据量大概为:1171 * 1171 * 16 = 21939856。

4.索引的分类

聚集索引选取规则:

  • 如果存在主键,主键索引就是聚集索引
  • 如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引
  • 如果表没有主键或没有合适的唯一索引,则 InnoDB 会自动生成一个 rowid 作为隐藏的聚集索引

5.有关索引的语法

--创建索引,如果不加索引类型参数,则创建的是常规索引
CREATE [ UNIQUE | FULLTEXT ] INDEX index_name ON table_name (index_col_name, ...);--查看索引
SHOW INDEX FROM 表名;--删除索引
DROP INDEX index_name ON 表名;例子:
mysql> desc test;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int         | NO   | PRI | NULL    |       |
| name  | varchar(10) | YES  |     | NULL    |       |
| age   | int         | NO   |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)mysql> create index idx_age on test(age);
Query OK, 0 rows affected (0.05 sec)
Records: 0  Duplicates: 0  Warnings: 0mysql> show create table test;
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                         |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| test  | CREATE TABLE `test` (`id` int NOT NULL,`name` varchar(10) DEFAULT NULL,`age` int NOT NULL,PRIMARY KEY (`id`),KEY `idx_age` (`age`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)mysql> show index from test;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| test  |          0 | PRIMARY  |            1 | id          | A         |           7 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| test  |          1 | idx_age  |            1 | age         | A         |           1 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
2 rows in set (0.01 sec)mysql> drop index idx_age on test;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0mysql> show index from test;      
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| test  |          0 | PRIMARY  |            1 | id          | A         |           7 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
1 row in set (0.01 sec)

6.索引失效的情况

内容也比较多,总结在该文章中

MySQL索引失效与MySQL8新添加的索引特性

7.MySQL中自动或人为优化索引的方法

覆盖索引优化

说到覆盖索引优化,那就要先讲回表查询。上图中的id是主键,name也创建了二级索引。但是上图的sql语句就需要在name二级索引中找到Arm,之后回到id索引再查找所有数据,这个就是回表查询

那如果查询语句是select id from user where name='Arm';这个只查找id不用回表查询,因为name索引树上是已有id值了。(二级索引树上数据都是主键值的)

也就是说,在这个查询里面,索引 name已经“覆盖了”我们的查询需求,我们称为覆盖索引

由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。

这个就需要人为地修改sql语句,尽量符合覆盖索引要求。

前缀索引

前缀索引顾名思义就是使用某个字段中字符串的前几个字符建立索引,那我们为什么需要使用前缀来建立索引呢?

使用前缀索引是为了减小索引字段大小,可以增加一个索引页中存储的索引值,有效提高索引的查询速度。在一些大字符串的字段作为索引时,使用前缀索引可以帮助我们减小索引项的大小。

但是,其也有缺点,使用前缀索引可能会增加扫描行数,这会影响到性能。还有对覆盖索引的影响。

来看个场景:有个表user,其中id是主键,name字段类型是varchar(20)。现在给字段name添加普通索引index1(name)或者 前缀索引index2(name(7))。

语句1

select id,username from user where username='zhangsan';

 语句2

select id,name,phone from user where name='zhangsan';

 语句2就多查了个phone字段。

所以,如果使用 index1(即 name整个字符串的索引结构)的话,可以利用覆盖索引,从 index1 查到结果后直接就返回了,不需要回到 ID 索引再去查一次。

而如果使用 index2(即 name(7) 索引结构)的话,就不得不回到 ID 索引再去判断 email 字段的值。

即使你将 index2 的定义修改为 name(20) 的前缀索引,这时候虽然 index2 已经包含了所有的信息,但 InnoDB 还是要回到 id 索引再查一下,因为系统并不确定前缀索引的定义是否截断了完整信息

即是,使用前缀索引就使用不上覆盖索引对查询性能的优化,这也是选择是否使用前缀索引时需要考虑的一个因素。

索引下推优化

在查询select * from user idcard liek '123%' and age=10;表user创建了联合索引(idcard,username,age)。

所以这个语句在搜索索引树的时候,只能用 “123”,找到第一个满足条件的记录 ID1。当然,这还不错,总比全表扫描要好。

然后呢?当然是判断其他条件是否满足。

在 MySQL 5.6 之前,只能从 ID1 开始一个个回表。到主键索引上找出数据行,再对比字段值。

而 MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数

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

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

相关文章

iTOP-3588开发板快速测试手册Android12系统功能测试

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

洛谷题单_递推与递归

P1255 数楼梯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) //不满分做法&#xff1a;没有高精度 #include <bits/stdc.h> using namespace std; const int N5006; int dp[N];//dp[i]表示到第i节楼梯有dp[i]中方案 int main(){int n;cin>>n;dp[1]1;dp[0]1;for(i…

MySQL(基础篇)——多表查询

一.多表关系 一对多(多对一) 多对多一对一 1.一对多(多对一) a.案例&#xff1a;部门与员工的关系 b.关系&#xff1a;一个部门对应多个员工&#xff0c;一个员工对应一个部门 c.实现&#xff1a;在多的一方建立外键&#xff0c;指向一的一方的主键 2.多对多 a.案…

Elasticsearch入门-环境安装ES和Kibana以及ES-Head可视化插件和浏览器插件es-client

Elasticsearch入门-环境安装ES和Kibana 安装 ES Windows安装ESHead安装浏览器插件 es-clientKibana 安装 安装es,安装header 安装kibana&#xff0c;安装多种分词器ik… 安装 ES Windows安装 ① 下载压缩包并解压官网链接&#xff1a;https://www.elastic.co/cn/downloads/ela…

canvas坐标系统 webgl坐标系统 uv纹理坐标系统 原点

一、canvas原点在左上角&#xff0c;x轴正方向向右&#xff0c;y轴正方向向下&#xff0c;一个点对应一个像素 二、webgl原点在正中间&#xff0c;x轴正方向向右&#xff0c;y轴正方向向上&#xff0c;数据显示范围在[-1,1]之间&#xff0c;超过此范围不显示数据 三、uv原点在左…

如何用ChatGPT+GEE+ENVI+Python进行高光谱,多光谱成像遥感数据处理?

原文链接&#xff1a;如何用ChatGPTGEEENVIPython进行高光谱&#xff0c;多光谱成像遥感数据处理&#xff1f; 第一&#xff1a;遥感科学 从摄影侦察到卫星图像 遥感的基本原理 遥感的典型应用 第二&#xff1a;ChatGPT ChatGPT可以做什么&#xff1f; ChatGPT演示使用 …

工厂模式:没你想像的那么难

工厂模式 工厂模式是一种创建型设计模式&#xff0c;它允许创建对象而无需指定将要创建的对象的具体类。它通过将对象的创建委托给一个单独的方法或类来完成&#xff0c;从而隐藏了对象的实例化逻辑。这样可以提高代码的灵活性&#xff0c;减少了代码中的重复和耦合。 在工厂…

2021年下半年教师资格证考试《高中信息技术》题

4.使用某转码软件对一段时长为2分钟的AVI视频进行转码&#xff0c;转码后的视频信息如图4所示&#xff0c;计算存储该视频文件所需的空间大小为&#xff08;C &#xff09;。 A18MB B36MB C60MB D512MB 6.某21位二进制代码100101011010011110101&#xff0c;已知该代码由3个…

html基础操练和进阶修炼宝典

文章目录 1.超链接标签2.跳锚点3.图片标签4.表格5.表格的方向属性6.子窗口7.音视频标签8.表单9.文件上传10.input属性 html修炼必经之路—各种类型标签详解加展示&#xff0c;关注点赞加收藏&#xff0c;防止迷路哦 1.超链接标签 <!DOCTYPE html> <html lang"en…

再议【每天进步一点点】

概述 之前听姜胡说&#xff0c;讲到了他自己日更博客的故事&#xff0c;也就是每天去更新一篇博客文章。 日更&#xff0c;其实是一件很可怕的事情。 先不说文章的深度如何&#xff0c;单单从时间的耗费上&#xff0c;文字的积累上&#xff0c;以及对事物的敏感度上&#xf…

vue实现自定义树形穿梭框功能

需求&#xff1a; 我们在开发过程中&#xff0c;会遇到需要将一个数据选择做成穿梭框&#xff0c;但是要求穿梭框左侧为树形结构、右侧为无层级结构的数据展示&#xff0c;ElementUI自身无法在穿梭框中添加树形结构&#xff0c;网上搜到了大佬封装的插件但是对于右侧的无树形结…

【从Python基础到深度学习】9.Python 语法基础

一、常量与变量 常量:程序中使用的具体的数、字符。在运行过程中&#xff0c;值无法更改 变量:表示一一个存储单元&#xff0c;其中存储的值可以修改 如&#xff1a;a5,b6 变量命名: 1、只能包含字母、数字、下划线 2、只能以字母、下划线开头 3、不要使用关键字作为变量名称 …

不知道伦敦银模拟账户该如何使用?至少3个用法

由于模拟交易的特别属性&#xff0c;很多人对模拟交易并不用心&#xff0c;假的资金用心干什么&#xff1f;就算交易得再好&#xff0c;盈利得再多&#xff0c;假的资金会变成真的吗&#xff1f;因此当然不会这么用心对待伦敦银模拟账户交易账户。实际上&#xff0c;这种观点是…

List集合的Stream流式操作实现数据类型转换

目录 问题现象&#xff1a; 问题分析&#xff1a; 解决方法&#xff1a; 拓展&#xff1a; 1、Collectors.toList() 2、Collectors.toCollection(ArrayList::new) 3、Collectors.toCollection(LinkedList::new) 4、Collectors.toCollection(LinkedHashSet::new) 5、Collector…

MAC M1 安装mongodb7.0.5 版本

1、进入官网 Download MongoDB Community Server | MongoDBDownload MongoDB Community Server non-relational database to take your next big project to a higher level!https://www.mongodb.com/try/download/community 2、选择版本 3、下载后解压 放到 /usr/local 并修改…

Facebook Messenger链接分享:如何创建链接并设置自动化内容

Facebook Messenger链接是指基于Facebook用户名创建的会话链接&#xff0c;用户可以在其Facebook页面的设置部分复制此链接进行分享。然后将该链接直接粘贴到独立站、电子邮件、名片或社交媒体中&#xff0c;让目标受众可以一键进入对话。为了满足某些商家的需求&#xff0c;Fa…

vue3中的ref和reactive的区别

vue3中的ref和reactive的区别 1、响应式数据2、ref3、reactive4、ref VS reactive5、往期回顾总结&#xff1a; 1、响应式数据 处理响应式数据时到底是该用ref还是reactive... 响应式数据是指在 Vue.js 中&#xff0c;当数据发生变化时&#xff0c;相关的视图会自动更新以反映…

【bash】2、手把手实现一个 bash shell:多个机器批量执行 shell 命令,支持 ip 补全

文章目录 一、需求&#xff1a;多台机器批量远程执行 shell 命令1.1 业务需求拆解为脚本需求1.2 帮助函数&#xff1a;使用说明文档1.3 main 函数框架 二、功能&#xff1a;单机 sshp 执行2.1 fullip 函数&#xff1a;实现 ip 补全2.1.1 参数说明2.1.2 定义全局变量2.1.3 实现&…

Pytorch 复习总结 4

Pytorch 复习总结&#xff0c;仅供笔者使用&#xff0c;参考教材&#xff1a; 《动手学深度学习》Stanford University: Practical Machine Learning 本文主要内容为&#xff1a;Pytorch 深度学习计算。 本文先介绍了深度学习中自定义层和块的方法&#xff0c;然后介绍了一些…

基于Beego 1.12.3的简单website实现

参考 用Beego开发web应用 https://www.cnblogs.com/zhangweizhong/p/10919672.htmlBeego官网 Homepage - beego: simple & powerful Go app frameworkbuild-web-application-with-golang https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/pr…