mysql的索引(一)
目录
- 索引是什么
- `mysql` 为何使用 `B+ Tree` 作为索引的数据结构
- 二叉树为什么不可行
- 平衡二叉树为什么不可行
- `B Tree`
- 为什么说 `B Tree` 能够解决平衡二叉树存在的问题呢
- `B+ Tree`
- `B Tree` 和 `B+ Tree` 区别
- `mysql` 为什么最终要去选择 `B+ Tree`
- `Innodb` 引擎的索引实现
- 聚集索引(主键索引)
- 普通索引(非主键索引)
- 聚集索引与普通索引示例
- 建表
- 索引 `B+ Tree` 的存储结构
- 普通索引(非主键索引)的 `B+ Tree` 的存储结构
- 聚集索引查找过程
- 普通索引查找过程
- 普通索引查找过程第一步
- 普通索引查找过程第二步
- 回表查询
- 普通索引存在的意义
索引是什么
索引是为了加速对表中数据的检索的一种数据结构,是 B+ Tree
的一种数据结构
索引保存数据的方式一般有两种:
- 数据区保存主键
id
对应行数据的所有数据具体内容 - 数据区保存的是真正保存数据的磁盘地址
其工作机制如下图:
上图中,如果现在有一条 sql
语句
select * from user where id = 40
- 如果没有索引的条件下,我们要找到这条记录,我们就需要在数据中进行全表扫描,匹配
id = 40
的数据 - 如果有了索引,我们就可以通过索引进行快速查找,如上图中,可以先在索引中通过
id = 40
进行二分查找,再根据定位到的地址取出对应的行数据
mysql
为何使用 B+ Tree
作为索引的数据结构
关于数据结构之树详情查看:https://blog.csdn.net/weixin_38192427/article/details/112973493
二叉树为什么不可行
对数据的加速检索,首先想到的就是二叉树,二叉树的查找时间复杂度可以达到 O(log2(n))
下面看一下二叉树的存储结构:
二叉树搜索相当于一个二分查找。二叉查找能大大提升查询的效率,但是它有一个问题:二叉树以第一个插入的数据作为根节点,如上图中,如果只看右侧,就会发现,就是一个线性链表结构。如果我们现在的数据只包含1, 2, 3, 4
,就会出现
如果我们要查询的数据为 4
,则需要遍历所有的节点才能找到 4
,即相当于全表扫描,就是由于存在这种问题,所以二叉查找树不适合用于作为索引的数据结构
平衡二叉树为什么不可行
为了解决二叉树存在线性链表的问题,会想到用平衡二叉查找树来解决
下面看看平衡二叉树是怎样的:
平衡二叉查找树定义为:节点的子节点高度差不能超过 1
,如上图中的节点 20
,左节点高度为 1
,右节点高度 0
,差为 1
,所以上图没有违反定义,它就是一个平衡二叉树。保证二叉树平衡的方式为左旋,右旋等操作
如果上图中平衡二叉树保存的是 id
索引,现在要查找 id = 8
的数据,过程如下:
- 把根节点加载进内存,用
8
和10
进行比较,发现8
比10
小,继续加载10
的左子树 - 把
5
加载进内存,用8
和5
比较,同理,加载5
节点的右子树 - 此时发现命中,则读取
id
为8
的索引对应的数据
到这里,平衡二叉树解决了存在线性链表的问题,数据查询的效率好像也还可以,基本能达到 O(log2(n))
, 那为什么 mysql
不选择平衡二叉树作为索引存储结构,他又存在什么样的问题呢
- 搜索效率不足:一般来说,在树结构中,数据所处的深度,决定了搜索时的
IO
次数(mysql
中将每个节点大小设置为一页大小,一次IO
读取一页或一个节点)。如上图中搜索id = 8
的数据,需要进行3
次IO
。当数据量到达几百万的时候,树的高度就会很恐怖 - 查询不稳定。如果查询的数据落在根节点,只需要一次
IO
,如果是叶子节点或者是支节点,会需要多次IO
才可以 - 存储的数据内容太少。没有很好利用操作系统和磁盘数据交换特性,也没有利用好磁盘
IO
的预读能力。因为操作系统和磁盘之间一次数据交换是以页为单位的,一页大小为4K
,即每次IO
操作系统会将4K
数据加载进内存。但是,在二叉树每个节点的结构只保存一个关键字,一个数据区,两个子节点的引用,并不能够填满4K
的内容。幸幸苦苦做了一次的IO
操作,却只加载了一个关键字。在树的高度很高,恰好又搜索的关键字位于叶子节点或者支节点的时候,取一个关键字要做很多次的IO
那有没有一种结构能够解决二叉树的这种问题呢?有,那就是多路平衡查找树
B Tree
B Tree
是一个绝对平衡树,所有的叶子节点在同一高度
如下图所示:
上图为一个 2-3
树(每个节点存储 2
个关键字,有 3
路),多路平衡查找树也就是多叉的意思,从上图中可以看出,每个节点保存的关键字的个数和路数关系为:关键字个数 =
路数 – 1
假设要从上图中查找 id = X
的数据,B TREE
搜索过程如下:
- 取出根磁盘块,加载
40
和60
两个关键字 - 如果
X
等于40
,则命中;如果X
小于40
走P1
;如果40 < X < 60
走P2
;如果X = 60
,则命中;如果X > 60
走P3
- 根据以上规则命中后,接下来加载对应的数据,数据区中存储的是具体的数据或者是指向数据的指针
为什么说 B Tree
能够解决平衡二叉树存在的问题呢
B Tree
能够很好的利用操作系统和磁盘的交互特性:mysql
为了很好的利用磁盘的预读能力,将页大小设置为16K
,即将一个节点(磁盘块)的大小设置为16K
,一次IO
将一个节点(16K
)内容加载进内存。这里,假设关键字类型为int
,即4
字节,若每个关键字对应的数据区也为4
字节,不考虑子节点引用的情况下,则上图中的每个节点大约能够存储(16 * 1000)/ 8 = 2000
个关键字,共2001
个路数。对于二叉树,三层高度,最多可以保存7
个关键字,而对于这种有2001
路的B 树
,三层高度能够搜索的关键字个数远远的大于二叉树
这里顺便说一下:在 B Tree
保证树的平衡的过程中,每次关键字的变化,都会导致结构发生很大的变化,这个过程是特别浪费时间的,所以创建索引一定要创建合适的索引,而不是把所有的字段都创建索引,创建冗余索引只会在对数据进行新增,删除,修改时增加性能消耗
B Tree
确实已经很好的解决了问题,我先这里先继续看一下 B+Tree
结构,再来讨论 B Tree
和 B+ Tree
的区别
B+ Tree
B+ Tree
是 B Tree
的一个变种,在 B+ Tree
中,B Tree
的路数和关键字的个数的关系不再成立了,数据检索规则采用的是左闭合区间,路数和关键个数关系为 1:1
具体如下图所示:
如果上图中是用 id
做的索引,如果是搜索 X = 1
的数据,搜索规则如下
- 取出根磁盘块,加载
1,28,66
三个关键字 X <= 1
走P1
,取出磁盘块,加载1,10,20
三个关键字X <= 1
走P1
,取出磁盘块,加载1,8,9
三个关键字- 已经到达叶子节点,命中
1
,接下来加载对应的数据,图中数据区中存储的是具体的数据
B Tree
和 B+ Tree
区别
B Tree
一个节点里存的是数据,而B+ Tree
存储的是索引(地址),所以B Tree
里一个节点存不了很多个数据,但是B+ Tree
一个节点能存很多索引,B+ Tree
叶子节点存所有的数据B+ Tree
的叶子节点是数据阶段用了一个链表串联起来,便于范围查找 通过B Tree
和B+ Tree
的对比我们看出,B+ Tree
节点存储的是索引,在单个节点存储容量有限的情况下,单节点也能存储大量索引,使得整个B+ Tree
高度降低,减少了磁盘IO
B+ Tree
的叶子节点是真正数据存储的地方,叶子节点用了链表连接起来,这个链表本身就是有序的,在数据范围查找时,更具备效率。因此mysql
的索引用的就是B+ Tree
,B+ Tree
在查找效率、范围查找中都有着非常不错的性能
mysql
为什么最终要去选择 B+ Tree
B+Tree
是B TREE
的变种,B TREE
能解决的问题,B+TREE
也能够解决(降低树的高度,增大节点存储数据量)B+Tree
扫库和扫表能力更强。如果我们要根据索引去进行数据表的扫描,对B TREE
进行扫描,需要把整棵树遍历一遍,而B+TREE
只需要遍历他的所有叶子节点即可(叶子节点之间有引用)B+TREE
磁盘读写能力更强。他的根节点和支节点不保存数据区,所以根节点和支节点同样大小的情况下,保存的关键字要比B TREE
要多。而叶子节点不保存子节点引用,能用于保存更多的关键字和数据。所以B+TREE
读写一次磁盘加载的关键字比B TREE
更多B+Tree
排序能力更强。上面的图中可以看出,B+Tree
天然具有排序功能B+Tree
查询性能稳定。B+Tree
数据只保存在叶子节点,每次查询数据,查询IO
次数一定是稳定的。当然这个每个人的理解都不同,因为在B TREE
如果根节点命中直接返回,确实效率更高
Innodb
引擎的索引实现
聚集索引(主键索引)
聚集索引: InnoDB
会使用主键 ID
建立索引 B+ Tree
,而其 B+ Tree
的叶子节点存储的是主键 ID
对应的数据,聚集索引的叶子节点称为数据页,聚集索引的这个特性决定了索引树中的数据也是索引的一部分
由此可见,使用聚集索引查询会很快,因为可以直接定位到行记录
- 如果表设置了主键,则主键就是聚集索引(主键索引)
- 如果表没有主键,则会默认第一个
NOT NULL
,且唯一(UNIQUE
)的列作为聚集索引(主键索引) - 以上都没有,则会默认创建一个隐藏的
row_id
作为聚集索引(主键索引)
普通索引(非主键索引)
普通索引:当表中创建了普通索引(非主键索引)时,InnoDB
就会建立普通索引 B+ Tree
,这个普通索引 B+ Tree
的叶子节点存储的是数据记录的主键 ID
,而与这个数据记录的主键 ID
所对应的就是普通索引(非主键索引)
聚集索引与普通索引示例
建表
mysql> create table user(-> id int(10) auto_increment,-> name varchar(30),-> age tinyint(4),-> primary key (id),-> index idx_age (age)-> )engine=innodb charset=utf8mb4;insert into user(name,age) values('张三',30);
insert into user(name,age) values('李四',20);
insert into user(name,age) values('王五',40);
insert into user(name,age) values('刘八',10);mysql> select * from user;
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | 张三 | 30 |
| 2 | 李四 | 20 |
| 3 | 王五 | 40 |
| 4 | 刘八 | 10 |
+----+--------+------+
id
字段是聚集索引,age
字段是辅助索引(普通索引)
索引 B+ Tree
的存储结构
id
是主键,所以是聚集索引,其叶子节点存储的是 id
主键对应的行记录的数据。聚集索引的这个特性决定了索引树中的数据也是索引的一部分
普通索引(非主键索引)的 B+ Tree
的存储结构
age
是普通索引(辅助索引),其叶子节点存储的是数据记录的主键 ID
,而与这个数据记录的主键 ID
所对应的就是普通索引(非主键索引)
聚集索引查找过程
如果查询条件为主键(聚集索引),则只需扫描一次 B+ Tree
即可通过聚集索引定位到要查找的行记录数据
select * from user where id = 1
普通索引查找过程
如果查询条件为普通索引(非聚集索引),需要扫描两次 B+ Tree
,第一次扫描通过普通索引定位到聚集索引的值,然后第二次扫描通过聚集索引的值定位到要查找的行记录数据
select * from user where age = 30;
- 先通过普通索引
age = 30
定位到主键值id = 1
- 再通过聚集索引
id = 1
定位到行记录数据
普通索引查找过程第一步
普通索引查找过程第二步
回表查询
先通过普通索引的值定位聚集索引值,再通过聚集索引的值定位行记录数据,需要扫描两次索引 B+ Tree
,它的性能较扫一遍索引树更低
普通索引存在的意义
- 聚集索引:
InnoDB
会使用主键ID
建立索引B+ 树
,而其B+ 树
的叶子节点存储的是主键ID
对应的数据 - 普通索引:普通索引
B+ 树
的叶子节点存储的是数据记录的主键ID
,而与这个数据记录的主键ID
所对应的就是聚集索引(主键索引)
因为 InnoDB
需要节省存储空间。一个表里可能有很多个索引,InnoDB
都会给每个加了索引的字段生成索引树,如果每个字段的索引树都存储了具体数据,那么这个表的索引数据文件就变得非常巨大(数据极度冗余了)。从节约磁盘空间的角度来说,真的没有必要为每个字段索引树都存具体数据,通过这种看似“多此一举”的步骤,在牺牲较少查询的性能下节省了巨大的磁盘空间,这是非常有值得的
mysql的索引(一)相关推荐
- 面试官问:为什么MySQL的索引不采用Kafka的索引机制
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 第一眼看到这个问题,也是很迷惑的,谁没事会问这种问题.然而,事实上 ...
- 面试官:为什么MySQL的索引要使用B+树,而不是其它树?比如B树?
点击上方"方志朋",选择"设为星标" 做积极的人,而不是积极废人 来源:https://dwz.cn/exC8JdQS InnoDB的一棵B+树可以存放多少行数 ...
- 为了把mysql的索引底层原理讲清楚,我把计算机翻了个底朝天
来自:非科班的科班 什么是索引 概念:索引是提高mysql查询效率的数据结构.总的一句话概括就是索引是一种数据结构. 数据库查询是数据库的最主要功能之一.设计者们都希望查询数据的速度能尽可能的快,因此 ...
- mysql多索引结构_MySQL 索引结构
谈到 MYSQL 索引服务端的同学应该是熟悉的不能再熟悉,新建表的时候怎么着都知道先来个主键索引,对于经常查询的列也会加个索引加快查询速度.那么 MYSQL 索引都有哪些类型呢?索引结构是什么样的呢? ...
- mysql repair 索引_mysql 创建索引、重建索引、查询索引、删除索引 转自:http://www.phpernote.com/mysql/942.html...
本篇文章主要是对MySQL索引操作方法做了一下总结,包括创建索引.重建索引.查询索引.删除索引的操作.以下所列示例中中 `table_name` 表示数据表名,`index_name` 表示索引名,c ...
- mysql简单索引_mysql简单索引
mysql的索引是在存储引擎实现的,而不是在服务器层,因此不是标准的. b-tree: 大部分的mysql支持b-tree索引,archive知道mysql5.1才支持,而且仅仅是支持单个auto_i ...
- mysql单列索引和多列索引_mysql 单列索引与多列索引
以下的文章主要介绍的是MySQL数据库索引类型,其中包括普通索引,唯一索引,主键索引与主键索引,以及对这些索引的实际应用或是创建有一个详细介绍,以下就是文章的主要内容描述. (1)普通索引 这是最基本 ...
- MySQL - 践行索引优化
文章目录 生猛干货 Pre Table Demo DB Version Case 全值匹配 最左前缀 禁止索引列上做任何操作(计算.函数.(自动or手动)类型转换) 存储引擎不能使用索引中范围条件右边 ...
- mysql+零时数据结构,MySql主要索引数据结构
索引数据结构 1. 二叉搜索树(Binary Search Tree) 二叉搜索树是每个节点最多有两个子节点的树,按照右侧子节点大于本节点,左侧子节点小于本节点的规律排列,可以用作搜索,结构如下图所示 ...
- mysql聚集索引可以多列吗_MySQL使用单列索引和多列索引
讨论MySQL选择索引时单列单列索引和多列索引使用,以及多列索引的最左前缀原则. 1. 单列索引 在性能优化过程中,选择在哪些列上创建索引是最重要的步骤之一.可以考虑使用索引的主要有两种类型的列:在W ...
最新文章
- 设计模式学习2:单例模式
- Codeforces Round #193 (Div. 2)
- server.mappath php,Server.MapPath( ) 方法的主要功能是获取文件的绝对路径。
- sqlalchemy query
- 服务器控件的使用注意事项
- 《数据挖掘导论》学习 | 第十章 异常检测
- 数据库和数据库实例的概念
- 每个国家对应的values语言Locale和国家代码对照表
- C语言统考试卷一及答案,2017年《计算机应用基础》统考试题及答案
- Okhttp上传图片
- 架构设计:负载均衡层设计方案(8)——负载均衡层总结上篇
- php后端上传文件,php实现文件上传方法_后端开发
- 邮购了正版蓝光碟《CODEnbsp;GEASS叛逆…
- HDU 4745 Two Rabbits——最长回文子串
- ubuntu 源更新
- 前端接入阿里云外呼SDK
- Django用admin开发的幼儿园薪资管理系统-2
- Verilog语言入门学习(3)
- 【大葱虽有4大治病功效】
- 唯品会不同阶段发展历程的技术演进
热门文章
- 自动驾驶 6-1: 横向车辆控制介绍 Lesson 1: Introduction to Lateral Vehicle Control
- 算法:Majority Element(求众数)
- 中山大学计算机学院官网万海,中山大学
- 2021-09-1311. 盛最多水的容器 数组 双指针
- 561. 数组拆分 I
- 从有序数组中找出某个数出现的次数
- matlab算薄板模态,基于MATLAB计算FGM薄板刚柔耦合动力学响应的仿真方法与流程
- Prometheus自动发现Exporter实现方案(一看就懂)
- 目标追踪论文之狼吞虎咽(1):VITAL算法
- 基础集合论笔记 目录