MySql Innodb 索引有哪些与详解

概述

对于MYSQL的INNODB存储引擎的索引,大家是不陌生的,都能想到是 B+树结构,可以加速SQL查询。但对于B+树索引,它到底“长”得什么样子,它具体如何由一个个字节构成的,这些的基础知识鲜有人深究。本篇文章从MYSQL行记录开始说起,层层递进,包括数据页,B+树聚簇索引,B+树二级索引,最后在文章末尾给出MYSQL索引的建议。

表空间

首先,我们来了解一下 MySQL 的表空间。在 MySQL 中,所有的数据都被存储在一个空间内,称之为表空间,表空间内部又可以分为段(segment)、区(extent)、页(page)、行(row),其逻辑结构如下图:

段(segment)

表空间是由不同的段组成的,常见的段有:数据段,索引段,回滚段等等,在 MySQL 中,数据是按照 B+ 树来存储,因此数据即索引,因此数据段即为 B+ 树的叶子节点,索引段为 B+ 树的非叶子节点,回滚段用于存储undo日志,用于事务失败后数据回滚以及在事务未提交之前通过undo日志获取之前版本的数据,在 InnoDB 1.1 版本之前,一个 InnoDB 只支持一个回滚段,支持 1023 个并发修改事务同时进行,在 InnoDB 1.2 版本,将回滚段数量提高到了 128 个,也就是说可以同时进行128 * 1023个并发修改事务。

区(extent)

区是由连续页组成的空间,每个区的固定大小为 1MB,为保证区中页的连续性,InnoDB 会一次从磁盘中申请 4 ~ 5 个区,在默认不压缩的情况下,一个区可以容纳 64 个连续的页。但是在开始新建表的时候,空表的默认大小为 96KB,是由于为了高效的利用磁盘空间,在开始插入数据时表会先利用 32 个页大小的碎片页来存储数据,当这些碎片使用完后,表大小才会按照 MB 倍数来增加。

页(page)

页是 InnoDB 存储引擎的最小管理单位,每页大小默认是 16KB,从 InnoDB 1.2.x 版本开始,可以利用innodb_page_size来改变页大小,但是改变只能在初始化 InnoDB 实例前进行修改,之后便无法进行修改,除非mysqldump导出创建新库,常见的页类型有:数据页、undo页、系统页、事务数据页、插入缓冲位图页、插入缓冲空闲列表页、未压缩的二进制大对象页以及压缩的二进制大对象页等。

行(row)

行对应的是表中的行记录,每页存储最多的行记录也是有硬性规定的最多16KB/2-200,即 7992 行,其中 16KB 是页大小。

Clustered Index 聚簇索引

MySQL InnoDB 引擎具有强制聚簇索引,通常使用主键。也就是主键就是Clustered Index,如果没有主键按以下规则生成。

Clustered Index 条件优化级:
  1. 表有明确的PRIMARY KEY:使用PRIMARY KEY

  2. 无PRIMARY KEY:InnoDB 默认使用第一个 UNIQUE INDEX,且索引列需要全部定义为非空列(NOT NULL)作为Clustered Index

  3. 如无PRIMARY KEY,也没有合适的UNIQUE INDEX,InnoDB将会在包含行ROW ID的合成列上生成一个名为GEN_CLUST_INDEX的隐藏Clustered Index

ROW ID:ROW ID是6 byte字段,由InnoDB分配,用于行排序。插入新行而单调增加,在物理上插入按ROW ID顺序排列

注:UNIQUE INDEX 包含的列需要全部定义为NOT NULL非空,才会被当做Clustered Index

MyISAM 存储引擎不支持聚簇索引并且一直使用堆表

2. 聚簇索引如何加速查询

通过聚簇索引访问行很快,因为索引搜索直接指向包含行数据的页面。如果表很大,与使用与索引记录不同的页来存储行数据的存储组织相比,聚簇索引架构通常可以节省磁盘 I/O 操作。

3. Clustered Index 示例及查询:

INNODB_INDEXES 表type字段说明:

  • 0 = 非唯一索引的二级索引 :nonunique secondary index;

  • 1 = 自动生成的聚簇索引:automatically generated clustered index (GEN_CLUST_INDEX);

  • 2 = 唯一索引(非聚簇索引): unique nonclustered index;

  • 3 = 聚簇索引 clustered index;

  • 32 = 全文索引 full-text index

不同MySQL版本表名不同,使用命令查询:SHOW TABLES FROM INFORMATION_SCHEMA LIKE 'INNODB_%';

自动生成名为GEN_CLUST_INDEX的Clustered Index示例:

-- 创建无主键、无唯一索引 
CREATE TABLE `clustered_index_demo` (`id` int DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;-- 查询表索引 
-- 如5.7以下版本表名不同,使用命令查询:SHOW TABLES FROM INFORMATION_SCHEMA LIKE 'INNODB_SYS%';
SELECT t2.INDEX_ID ,t2.`NAME` , t2.TABLE_ID , t2.`TYPE` , t2.N_FIELDS , t2.PAGE_NO , t2.`SPACE` , t2.MERGE_THRESHOLD
FROM information_schema.INNODB_TABLES t1 
INNER JOIN information_schema.INNODB_INDEXES t2 ON t1.TABLE_ID = t2.TABLE_ID
WHERE t1.`NAME` = 'wiki/clustered_index_demo';-- 查询结果| INDEX_ID | NAME            | TABLE_ID | TYPE | N_FIELDS | PAGE_NO | SPACE | MERGE_THRESHOLD |
|----------|-----------------|----------|------|----------|---------|-------|-----------------|
|     3616 | GEN_CLUST_INDEX |     3276 |    1 |        5 |       4 |  2113 |  
增加包含NOT NULL列的唯一索引示例:

Tips : 修改表结构,InnoDB将删除原自动生成的GEN_CLUST_INDEX索引

-- 增加两列
ALTER TABLE `wiki`.`clustered_index_demo`
ADD COLUMN `username` varchar(32) NOT NULL,
ADD COLUMN `name` varchar(64) NOT NULL;
-- 增加唯一索引 
ALTER TABLE `wiki`.`clustered_index_demo`
ADD UNIQUE INDEX `IDX_UNIQUE` (`username`,`name`) USING BTREE;| INDEX_ID | NAME       | TABLE_ID | TYPE | N_FIELDS | PAGE_NO | SPACE | MERGE_THRESHOLD |
|----------|------------|----------|------|----------|---------|-------|-----------------|
|     3620 | IDX_UNIQUE |     3278 |    3 |        5 |       4 |  2115 |              50 |
唯一索引包含NULL列
-- 将唯一索引,其中一列改为NULL, Clustered Index将被删除,重新生成GEN_CLUST_INDEX
ALTER TABLE `wiki`.`clustered_index_demo`
CHANGE `username` `username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
CHANGE `name` `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL;| INDEX_ID | NAME            | TABLE_ID | TYPE | N_FIELDS | PAGE_NO | SPACE | MERGE_THRESHOLD |
|----------|-----------------|----------|------|----------|---------|-------|-----------------|
|     3625 | GEN_CLUST_INDEX |     3281 |    1 |        6 |       4 |  2118 |              50 |
|     3626 | IDX_UNIQUE      |     3281 |    2 |        3 |       5 |  2118 |      

查询所有自动生成的Clustered Index

SELECTi.TABLE_ID,t.NAME
FROMinformation_schema.INNODB_INDEXES iJOIN information_schema.INNODB_TABLES t ON (i.TABLE_ID = t.TABLE_ID)
WHEREi.NAME = 'GEN_CLUST_INDEX';| TABLE_ID | NAME                      |
|----------|---------------------------|
|     3281 | wiki/clustered_index_demo |

 辅助索引

除了聚簇索引之外的索引都可以称之为辅助索引,与聚簇索引的区别在于辅助索引的叶子节点中存放的是主键的键值。一张表可以存在多个辅助索引,但是只能有一个聚簇索引,通过辅助索引来查找对应的航记录的话,需要进行两步,第一步通过辅助索引来确定对应的主键,第二步通过相应的主键值在聚簇索引中查询到对应的行记录,也就是进行两次 B+ 树搜索。相反,通过辅助索引来查询主键的话,遍历一次辅助索引就可以确定主键了,也就是所谓的索引覆盖,不用回表。

创建辅助索引,可以创建单列的索引,也就是用一个字段来创建索引,也可以用多个字段来创建副主索引称为联合索引,创建联合索引后,B+ 树的节点存储的键值数量不是 一个,而是多个,如下图:

  • 联合索引的 B+ 树和单键辅助索引的 B+ 树是一样的,键值都是排序的,通过叶子节点可以逻辑顺序的读出所有的数据,比如上图所存储的数据时,按照(a,b)这种形式(1,1),(1,2),(2,1),(2,4),(3,1),(3,2)进行存放,这样有个好处,那就是存放数据时排序了,当进行order by对某个字段进行排序时,可以减少复杂度,加速进行查询;

  • 当用select * from table where a=? and ?可以使用索引(a,b)来加速查询,但是在查询时有一个原则,SQL 的where条件的顺序必须和二级索引一致,而且还遵循索引最左原则,select * from table where b=?则无法利用(a,b)索引来加速查询。

  • 辅助索引还有一个概念便是索引覆盖,索引覆盖的一个好处便是辅助索引不包含行记录,因此其大小远远小于聚簇索引,利用辅助索引进行查询可以减少大量的 IO 操作。

索引的优缺点及建议

 

优点:

  1. 对于等值查询,可快速定位到对于的行记录。

  2. 对于范围查询,可辅助缩小扫描区间。

  3. 当ORDER BY的列名 与 索引的列名完全一致时,可加快排序的顺序。

  4. 当GROUP BY的列名 与 索引的列名完全一致时,可加快分组。

  5. 当二级索引列中 包含了 SELECT 关键字后面写明的所有列,则在查询完成二级索引之后无需进行回表操作,直接返回即可。这种情况,称为【覆盖索引】。

缺点:

建立索引占用磁盘空间。

对表中的数据进行 增加,删除,修改 操作时,都需要修改各个索引树,特别是如果新增的行记录的主键顺序不是递增的,就会产生页分裂,页回收等操作,有较大的时间成本。

当二级索引列的值 的 不重复值的个数较少时,通过二级索引查询找到的数据量就会比较多,相应的就会产生过多的回表操作。

在执行查询语句的时候,首先要生成一个执行计划。通常情况下,一个SQL在执行过程中最多使用一个二级索引,在生成执行计划时需要计算使用不同索引执行查询时所需的成本,最后选择成本最低的那个索引执行查询。因此,如果建立太多的索引,就会导致成本分析过程耗时太多,从而影响查询语句的性能。

建议:

  1. 只为用于搜索,排序,分组的列创建索引。

  2. 索引的列需要有辨识性,尽可能地区分出不同的记录。

  3. 索引列的类型尽量小。因为数据类型越小,索引占用的存储空间就越少,在一个数据页内就可以存放更多的记录,磁盘I/O带来的性能损耗也就越小。

  4. 如果需要对很长的字段进行快速查询,可考虑为列前缀建立索引。【alter table table_M add index idx_key1(column_n(10)) -->  将table_M表的 idx_key1列的前10个字符创建索引】

  5. 覆盖索引,当二级索引列中包含了SELECT关键字后面写明的所有列,则在查询完成二级索引之后无需进行回表操作,直接返回即可。因此,编写【select *】的时候,要想想是否必要

  6. 在查询语句中,索引列不要参与条件值计算,也是把条件值计算完成之后,再和索引列对比。【否则MYSQL会认为搜索条件不能形成合适的扫描区间来减少扫描的记录数量】

 

 

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

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

相关文章

【Spring Boot】JPA 的查询方式

JPA 的查询方式 1.使用约定方法名2.用 JPQL 进行查询3.用原生 SQL 进行查询3.1 根据 ID 查询用户3.2 查询所有用户3.3 根据 email 查询用户3.4 根据 name 查询用户,并返回分页对象 Page3.5 根据名字来修改 email 的值3.6 使用事务 4.用 Specifications 进行查询5.用…

Mac视频下载工具,兼容14系统,Downie 4软件下载

Downie 4 是一款由James Application开发的视频下载软件,支持Mac操作系统。该软件允许用户从各种视频网站上下载视频内容,以便于在本地设备上观看,无需依赖互联网连接。自动下载:可以设置Downie 4自动下载指定网站上的视频&#x…

当+=的时候,为什么会出现NaN?

问: var textToDisplay; // "testing"; textToDisplay "testing"; textToDisplay 1; var someNumber 1; var oneMoreNumber; oneMoreNumber textToDisplay someNumber; //results in NaN console.log(oneMoreNumber); 这里的结果是NaN? 回答: 是…

【LinuxC语言】线程池的原理和实现

文章目录 前言为什么需要线程池线程池的原理总结前言 在现代计算中,多线程编程已经成为一种常见的做法,它可以帮助我们更有效地利用多核处理器的能力。然而,频繁地创建和销毁线程会带来一定的开销。为了解决这个问题,我们可以使用一种称为“线程池”的技术。线程池是一种在…

黑马点评-Redis的缓存击穿,缓存雪崩,缓存穿透,互斥锁,逻辑过期

文章目录 1.缓存穿透2.缓存雪崩3.缓存击穿3.1 互斥锁3.2 基于逻辑过期 1.缓存穿透 解决办法 写入NULL值到Redis缓存,以后就会命中Redis的控制缓存而不会出现请求直接打到数据库的问题! 代码 2.缓存雪崩 这个概念很好理解,雪崩就是无数的…

【LLM大模型书】从零开始大模型开发与微调:基于PyTorch与ChatGLM (附PDF)

今天又来给大家推荐一本大模型方面的书籍<从零开始大模型开发与微调&#xff1a;基于PyTorch与ChatGLM>。 本书使用PyTorch 2.0作为学习大模型的基本框架&#xff0c;以ChatGLM为例详细讲解大模型的基本理论、算法、程序实现、应用实战以及微调技术&#xff0c;为读者揭…

设备树在Linux系统的属性

设备树源文件 设备树源文件扩展名为.dts&#xff0c;我们在前面移植 Linux 的时候却一直在使用.dtb 文件&#xff0c;那么 DTS 和 DTB 这两个文件是什么关系呢&#xff1f; DTS 是设备树源码文件&#xff0c; DTB 是将 DTS 编译以后得到的二进制文件。将.dts 编译为.dtb 需要什…

【微信小程序开发实战项目】——如何制作一个属于自己的花店微信小程序(2)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

FreeRTOS和UCOS操作系统使用笔记

FreeRTOS使用示例 UCOS使用示例 信号量使用 信号量访问共享资源区/ OS_SEMMY_SEM; //定义一个信号量&#xff0c;用于访问共享资源OSSemCreate ((OS_SEM* )&MY_SEM, //创建信号量&#xff0c;指向信号量(CPU_CHAR* )"MY_SEM", //信号量名字(OS_SEM_CTR )1, …

软件模型分类及特点

在软件开发的世界里&#xff0c;我们经常会遇到业务模型、系统模型和软件模型这三个层次。这些模型各有特点&#xff0c;相互之间也有着紧密的联系。通过理解这三个层次之间的映射关系&#xff0c;我们能更好地理解和掌握软件开发的全过程 1. 业务模型 业务模型描述了组织的业…

政务单位网站SSL证书选择策略

在数字化快速发展的今天&#xff0c;政务单位网站作为政府与公众沟通的重要桥梁&#xff0c;其安全性和可信度显得尤为重要。SSL证书作为保障网站安全的重要手段&#xff0c;其选择对于政务单位网站来说至关重要。本文将探讨政务单位网站在选择SSL证书时应该考虑的因素&#xf…

如何使用python网络爬虫批量获取公共资源数据教程?

原文链接&#xff1a;如何使用python网络爬虫批量获取公共资源数据教程&#xff1f;https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247608240&idx4&snef281f66727afabfaae2066c6e92f792&chksmfa826657cdf5ef41571115328a09b9d34367d8b11415d5a5781dc4c…

【AI提升】如何使用大模型:本机离线和FastAPI服务调用

大模型本身提供的功能&#xff0c;类似于windows中的一个exe小工具&#xff0c;我们可以本机离线调用然后完成具体的功能&#xff0c;但是别的机器需要访问这个exe是不可行的。常见的做法就是用web容器封装起来&#xff0c;提供一个http接口&#xff0c;然后接口在后端调用这个…

KV260视觉AI套件--PYNQ-DPU-Resnet50

目录 1. 简介 2. 代码解析 3. 全部代码展示 4. 总结 1. 简介 本文以 Resnet50 为例&#xff0c;展示使用 PYNQ 调用 DPU 运行 Resnet50 网络的详细过程&#xff0c;并对其中关键代码做出解释。 PYNQ是一个针对Xilinx Zynq平台的Python开发框架&#xff0c;它允许开发者使…

KEYSIGHT是德科技 E5063A ENA 系列网络分析仪

E5063A ENA 矢量网络分析仪 18GHz 2端口 降低无源射频元器件的测试成本 Keysight E5063A ENA 是一款经济适用的台式矢量网络分析仪&#xff0c;可用于测试简单的无源元器件&#xff0c;例如频率最高达到 18 GHz 的天线、滤波器、电缆或连接器。 作为业界闻名的 ENA 系列…

深入解析 Laravel 事件系统:架构、实现与应用

Laravel 的事件系统是框架中一个强大且灵活的功能&#xff0c;它允许开发者在应用程序中定义和使用自定义事件和监听器。这个系统基于观察者模式&#xff0c;使得代码解耦和可维护性大大提高。在本文中&#xff0c;我们将深入探讨 Laravel 事件系统的工作原理、如何实现自定义事…

python @装饰器的用法

装饰器&#xff08;decorators&#xff09;是 Python 中的一种高级特性&#xff0c;它允许开发者修改函数或方法的行为&#xff0c;而不改变其定义。装饰器通常用于日志记录、权限检查、性能测量等场景。装饰器是通过在函数定义的前一行加上 decorator_name 来使用的。 基本用…

Qt简单文本查找

Qt版本&#xff1a; Qt6 具体代码&#xff1a; 1. 头文件 mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow>class QLineEdit; class QDialog; class QPushButton; class QVBoxLayout; class QTextEdit;QT_BEGIN_NAMESPACE namespace Ui…

为什么AI算法工程师要求C++?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「c&#xff0b;&#xff0b;的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“666”之后私信回复“666”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;能跑出…

找到字符串中所有子串出现的位置python

直接find干就完了。 如果你希望找到字符串中所有子串出现的位置&#xff0c;而不仅仅是一个位置&#xff0c;你可以通过循环查找并收集所有起始位置。以下是修改后的代码&#xff1a; def find_all_substring_positions(string, substring): positions [] # 用于存储所有…