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: 0


mysql> 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: 0

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       |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
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), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue3创建h5 项目使用rem做响应式的配置

第一步 安装依赖&#xff1a; npm install amfe-flexible -S npm install postcss-px2rem -S第二步 main.ts文件中导入 import "amfe-flexible/index.js";第三步 进行配置&#xff1a; vue3 项目中创建 postcss.cinfig.js文件&#xff0c;这里是基于设计稿是750px…

好用的免费的文章一键生成神器

在当今信息爆炸的时代&#xff0c;创作内容是数字营销、网站运营等领域的核心。为了满足日益增长的内容需求&#xff0c;许多GPT生成的软件应运而生&#xff0c;它们能够帮助用户快速生成各种类型的文章&#xff0c;为创作提供了便利和效率。本文将介绍8款不同的免费文章一键生…

springboot3.x 以上,官方不建议使用spring.factories

springboot2.7.x 以上,官方不建议使用spring.factories 最近公司项目升级.需要将springcloud/springboot版本升级到2.7.x以上,再升级的过程中遇到了太多的问题.总结在了如下文章中: springboot艰难版本升级之路!! springboot 2.3.x版本升级到2.7.x版本 这篇文章就重点是梳理一…

力扣180 连续出现的数字

如何有效地识别在数据库中至少连续出现三次的数字&#xff1f; 目录 题目描述 解题思路 完整代码 进一步探索 题目描述 表&#xff1a;Logs ---------------------- | Column Name | Type | ---------------------- | id | int | | num | varch…

安科瑞智慧消防产品在酒店行业的应用

摘要&#xff1a;进入 21 世纪以来&#xff0c;随着网络技术的发展&#xff0c;世界经济呈现全球化趋势&#xff0c;作为消费者衣食住行的重要组成部分&#xff0c;酒店行业也是生活服务市场重要的组成部分。在经济发展的环境下&#xff0c;在旅游业兴起的趋势下&#xff0c;酒…

vue系列——vscode,node.js vue开发环境搭建

第一步安装node.js 推荐使用nvm进行node.js 的安装 nvm(Node.js version manager) 是一个命令行应用&#xff0c;可以协助您快速地 更新、安装、使用、卸载 本机的全局 node.js 版本。 可以去网上查找相关版本 我这里使用 nvm-setu… 链接:https://pan.baidu.com/s/1UEUtmzw5x…

私域运营的7个关键策略

1.通过私域工具实现标签&#xff0c;话术&#xff0c;聚合聊天等 2、公域获客&#xff0c;私域精准化运营&#xff0c;变现解决方案 3、标签化运营&#xff0c;筛选不同层级客户&#xff0c;根据对方需求提供解决方案 4、做私域需要坚持&#xff0c;不断累计信任 5、高复购…

matplotlib——折线图(python)

前言 在学习数据分析前&#xff0c;一定要有python基础&#xff0c;不一定要非常熟练的掌握这门语言&#xff0c;但一定要对python有个大概的了解&#xff08;就像我一样&#xff0c;大一学过python&#xff0c;但是现在忘的差不多了&#xff0c;但是没关系&#xff0c;有个大…

MYSQL 解释器小记

解释器的结果通常通过上述表格展示&#xff1a; 1. select_type 表示查询的类型 simple: 表示简单的选择查询&#xff0c;没有子查询或连接操作 primary:表示主查询&#xff0c;通常是最外层的查询 subquery :表示子查询&#xff0c;在主查询中嵌套的查询 derived: 表示派…

vue2 开发记录

el-select 如何修改选择项的样式/el-select-dropdown__item 文字上下显示 测试代码 <div stylemargin-left: 100px><!-- 测试代码--><el-select filterablesizemini><div classxxx-el-select><el-optionv-foritem in [{key:1,des:2,…

构建产业图谱,实现产业链精准招商!

​产业链招商是一种以产业链为基础的招商引资方式&#xff0c;它不仅仅关注单个企业的引入&#xff0c;而是侧重于整个产业链的布局和发展&#xff0c;通过吸引产业链上下游相关企业共同入驻&#xff0c;形成产业集群&#xff0c;从而提升地区或园区的产业竞争力和经济效益&…

数据之光:探索数据库技术的演进之路

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。&#x…

Linux - 基本指令

1、ls 指令 语法&#xff1a;ls [选项][目录或文件] 功能&#xff1a;对于目录&#xff0c;该命令列出该目录下的所有子目录与文件。对于文件&#xff0c;将列出文件名以及其他信息。 常用选项&#xff1a; -a 列出目录下的所有文件&#xff0c;包括以 . 开头的隐含文件-l …

重塑计算:ICP 打造无限智能合约云解决方案

作者&#xff1a;Lynn Cadet 编译&#xff1a;TinTinLand 原文&#xff1a;https://www.hostingadvice.com/blog/internet-computer-offers-a-limitless-smart-contract-cloud-solution/ 摘要&#xff1a;与其前身互联网一样&#xff0c;区块链行业寻求引领一个新时代及其经济…

力扣-跳跃游戏

问题 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 解答 class Solu…

linux系统Jenkins工具添加自由项目和maven项目

Jenkins添加自由项目 添加自由项目操作流程代码远程代码邮件标题邮件正文 添加maven项目准备环境操作流程 添加自由项目 gitlab配置基本代码页面等&#xff0c;拉取代码&#xff0c;打包&#xff0c;发布操作流程 代码 远程代码 echo ssh root192.168.188.177 "tar cz…

C++的常用排序(未完待续)

注&#xff1a;本文以升序为例 一、冒泡排序 1.1 操作方法 步骤1比较相邻元素&#xff0c;如果前者比后者大&#xff0c;则交换它们。步骤2对头到尾&#xff0c;对所有元素按序执行一轮这样的操作(这样可以找到第一最大值)步骤3再从第一个元素开始&#xff0c;重复上述比较操…

数据可视化基础与应用-02-基于powerbi实现连锁糕点店数据集的仪表盘制作

总结 本系列是数据可视化基础与应用的第02篇&#xff0c;主要介绍基于powerbi实现一个连锁糕点店数据集的仪表盘制作。 数据集描述 有一个数据集&#xff0c;包含四张工作簿&#xff0c;每个工作簿是一张表&#xff0c;其中可以销售表可以划分为事实表&#xff0c;产品表&am…

Python 在Word中查找并高亮指定文本

当你需要在长文档或报告中快速找到特定的关键词或短语&#xff0c;Word中提供的查找并高亮这一功能可以帮助你迅速定位这些内容。本文将介绍如何使用Python在Word中查找并突出显示指定的文本。 所需工具&#xff1a;第三方库 Spire.Doc for Python。该库支持创建、编辑、转换Wo…

笔记72:关于IMU(惯性测量单元)传感器的作用【不涉及公式推导】

一、IMU传感器是什么&#xff1a; 惯性测量单元IMU&#xff08;Inertial Measurement Unit&#xff09;是一种使用【加速度计】和【陀螺仪】来测量【物体三轴姿态角&#xff08;空间姿态&#xff09;】的装置&#xff1b;IMU在坐标系的每个坐标轴上&#xff0c;均安装有1个陀螺…