史上最全的Mysql索引教程

news2024/11/15 6:54:18

一、什么是索引

1.1 索引简介

索引是数据库中用来提高数据检索效率的数据结构。它类似于书籍的目录,可以帮助用户快速找到所需的数据,而不必扫描整个数据集。在数据库系统中,索引可以显著提高查询性能。

所谓的存储引擎,说白了就是如何存储数据、如何为存储的数据建立索引和如何更新、查询数据等技术的实现方法。MSQL存储引擎有 MyISAM、InnoDB、Memory,其中 InnoDB 是在 MySQL 5.5 之后成为默认的存储引擎。

以下是简化版的mysql的结构图,其中,索引和数据是存储在存储引擎中的。

1.2 一条sql语句是如何执行的

我们先从mysql的结构层面上帝视角初步的了解一下sql语句是如何从一条sql语法的发送到数据返回的,我们可以初步的看下下面的执行流程图:

简单的来说就是客户端发送一个连接请求和sql语句给service层,service就有对应的对于sql语句的初步的处理过程,到最后就会由执行器发送执行调用存储引擎拿到数据返回给客户端,下面我们详细的介绍每一步都是在干什么。

1.2.1 连接器

第一步:客户端发起连接,客户端首先肯定是需要连接到mysql服务才可以执行sql语句的,连接语句如下:

-u 指定用户
-p 指定密码,密码可以在交互对话框中输入
mysql -uroot -p

 上面就是连接成功到mysql的服务,如果用户密码都没有问题,连接器就会获取该用户的权限,然后保存起来,后续该用户在此连接里的任何操作,都会基于连接开始时读到的权限进行权限逻辑的判断。

那我们怎么可以看到mysql被多少个客户端连接呢?

 我们可以通过show processlist 命令进行查看。

 一个连接可以永久保存吗?一个连接可以保存多久?

一个连接当然无法被永久保存,MySQL 定义了空闲连接的最大空闲时长,由 wait_timeout 参数控制的,默认值是 8 小时(28880秒),如果空闲连接超过了这个时间,连接器就会自动将它断开。

 

当然我们在有的时候也会遇到mysql会有死锁导致线程一直等待的问题,这些问题是在mysql的命令中是无法解决的所以我们也可以自己手动的kill进程。

命令:kill connection + id

居然有了连接器,那我们想一个问题mysql可以支持无限的客户端进行连接吗? 

当然不可以支持无限的客户端连接,MySQL 服务支持的最大连接数由 max_connections 参数控制,如果超过这个值,系统就会拒绝接下来的连接请求,并报错提示“Too many connections”。

 

1.2.2 查询缓存

连接器的工作完成后,客户端就可以向 MySQL 服务发送 SQL 语句了,MySQL 服务收到 SQL 语句后,就会解析出 SQL 语句的第一个字段,看看是什么类型的语句。

如果 SQL 是查询语句(select 语句),MySQL 就会先去查询缓存( Query Cache )里查找缓存数据,看看之前有没有执行过这一条命令,这个查询缓存是以 key-value 形式保存在内存中的,key 为 SQL 查询语句,value 为 SQL 语句查询的结果。

如果查询的语句命中查询缓存,那么就会直接返回 value 给客户端。如果查询的语句没有命中查询缓存中,那么就要往下继续执行,等执行完后,查询的结果就会被存入查询缓存中。

如果是对于更新操作不是很频繁数据的变化比较少的表,缓存可以大大的加快查询的效率,如果是对于更新比较频繁的表,查询缓存的命中率很低的,因为只要一个表有更新操作,那么这个表的查询缓存就会被清空。如果刚缓存了一个查询结果很大的数据,还没被使用的时候,刚好这个表有更新操作,查询缓冲就被清空了,相当于缓存了个寂寞。

所以,MySQL 8.0 版本直接将查询缓存删掉了,也就是说 MySQL 8.0 开始,执行一条 SQL 查询语句,不会再走到查询缓存这个阶段了。

对于 MySQL 8.0 之前的版本,如果想关闭查询缓存,可以通过将参数 query_cache_type 设置成 DEMAND。

1.2.3 解析sql

在正式执行 SQL 查询语句之前, MySQL 会先对 SQL 语句做解析,这个工作交由「解析器」来完成,在所有的编程语言都会经过这一步。

解析器

第一步:词法分析。MySQL 会根据你输入的字符串识别出关键字出来,例如,SQL语句 select username from userinfo,在分析之后,会得到4个Token,其中有2个Keyword,分别为select和from:
第二步:语法分析。根据词法分析的结果,语法解析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法,如果没问题就会构建出 SQL 语法树,这样方便后面模块获取 SQL 类型、表名、字段名、 where 条件等等。

对于SELECT name, age FROM students WHERE age > 18;sql语句可能的语法树结构为:

- Query

        - SelectClause

                - ColumnRef(name)

                - ColumnRef(age)

- FromClause

        - TableRef(students)

- WhereClause

        - BinaryOp(>)

                - ColumnRef(age)

                - Value(18)

1.2.4 执行sql

经过解析器后,接着就要进入执行 SQL 查询语句的流程了,每条SELECT 查询语句流程主要可以分为下面这三个阶段:

  • prepare 阶段,也就是预处理阶段;

  • optimize 阶段,也就是优化阶段;

  • execute 阶段,也就是执行阶段;

预处理器

我们先来说说预处理阶段做了什么事情。

  • 检查 SQL 查询语句中的表或者字段是否存在;

  • 将 select * 中的 * 符号,扩展为表上的所有列;

如果sql语句中有不存在的表和字段就会在这个阶段报错。

优化器

优化器主要负责将 SQL 查询语句的执行方案确定下来,比如在表里面有多个索引的时候,优化器会基于查询成本的考虑,来决定选择使用哪个索引。

要想知道优化器选择了哪个索引,我们可以在查询语句最前面加个 explain 命令,这样就会输出这条 SQL 语句的执行计划,然后执行计划中的 key 就表示执行过程中使用了哪个索引,比如下图的 key 为 PRIMARY 就是使用了主键索引。

如果我现在使用到name字段进行查询的话explain会选择哪个扫描方式,如下图所示,下面是key为null而且type也为all所有走的是全表查询。

如果我现在给name加上一个普通的索引,key是什么。

索引现在就是我创建的idx_name而且使用到的type是ref普通索引。

后面就是执行语句调用api访问存储引擎拿到数据了。

到这个地方我们就知道了一条sql语句在mysql的底层中是如何执行的,我们就可以详细的来看一下索引部分的知识了。

二、索引的分类

我们可以按照四个角度来分类索引。
按「数据结构」分类:B+tree索引、Hash索引、Full-text索引。
按「物理存储」分类:聚簇索引(主键索引)、二级索引(辅助索引)
按「字段特性」分类:主键索引、唯一索引、前缀索引、普通索引。
按「字段个数」分类:单列索引、联合索引。

2.1 按照数据结构分类

1.B+Tree索引:这是MySQL中最常用的索引类型,几乎所有的存储引擎都支持。B+Tree索引适用于全值匹配、范围查询、排序和分组查询等操作。B+Tree索引在InnoDB和MyISAM存储引擎中的实现略有不同。InnoDB的B+Tree索引是聚集索引,数据和索引是存储在一起的,而MyISAM的B+Tree索引是非聚集索引,索引和数据是分开存储的。B+Tree索引由于其结构特点,支持范围查询和顺序访问,效率较高,下面的图中为了方便双向链表就用一个箭头表示了。

2.HASH索引:只有Memory存储引擎支持HASH索引,它适用于等值查询,但不支持范围查询。HASH索引通过哈希函数将键值转换为索引值,然后存储在哈希表中。由于哈希表的特性,HASH索引的查询速度非常快,但是它不支持索引值的顺序访问,也不支持部分索引列查找和范围查询。

3.Full-Text索引:主要用于文本数据的全文搜索,从MySQL 5.6开始,InnoDB和MyISAM存储引擎都支持Full-Text索引。Full-Text索引通过倒排索引的方式,可以快速匹配文档中的关键词。Full-Text索引适用于搜索大量文本数据,但是它可能会占用较多的磁盘空间,并且创建和维护索引的速度相对较慢。

2.2 按照物理存储分类

从物理存储的角度来看,索引分为聚簇索引(主键索引)、二级索引(辅助索引)。

  • 主键索引的 B+Tree 的叶子节点存放的是实际数据,所有完整的用户记录都存放在主键索引的 B+Tree的叶子节点里;
  • 二级索引的 B+Tree 的叶子节点存放的是主键值,而不是实际数据。

所以,在查询时使用了二级索引,如果查询的数据能在二级索引里查询的到,那么就不需要回表,这个过程就是覆盖索引。如果查询的数据不在二级索引里,就会先检索二级索引,找到对应的叶子节点,获取到主键值后,然后再检索主键索引,就能查询到数据了,这个过程就是回表。

所以回表查询的过程其实就是:首先在二级索引中查询到主键值,然后在到聚餐索引中去查找拿到数据。

2.3 按照字段个数分类

从字段个数的角度来看,索引分为单列索引、联合索引(复合索引)

  • 建立在单列上的索引称为单列索引,比如主键索引;
  • 建立在多列上的索引称为联合索引;

联合索引

create index idx_name_age on user(name,id);

 下面是联合索引的数据结构,联合索引是按照顺序保证局部顺序性,如果我是按照id和name的顺序进行排序的,那就是先把id进行排序好,然后在排序好的id中进行name排序。

因为联合索引是满足的局部顺序性,因此,使用联合索引时,存在最左匹配原则,也就是按照最左优先的方式进行索引的匹配。在使用联合索引进行查询的时候,如果不遵循「最左匹配原则」,联合索引会失效,这样就无法利用到索引快速查询的特性了。

比如,如果创建了一个(a,b,c)联合索引,如果査询条件是以下这几种,就可以匹配上联合索引:

  • where a=1;
  • where a=1 and b=2 and c=3;
  • where a=1 and b=2;

需要注意的是,因为有查询优化器,所以a字段在 where 子句的顺序并不重要。也就是说对于sql语句where b=1 and a=2;也会生效,我们关注的只是在where语句中的左边的字段有没有。
但是,如果查询条件是以下这几种,因为不符合最左匹配原则,所以就无法匹配上联合索引,联合索引就会失效:

  • where b=2;
  • where c=3;
  • where b=2 and c=3;

2.4 按照字段特性分类

从字段特性的角度来看,索引分为主键索引、唯一索引、普通索引、前缀索引。

主键索引
主键索引就是建立在主键字段上的索引,通常在创建表的时候一起创建,一张表最多只有一个主键索引索引列的值不允许有空值。

在创建表时,创建主键索引的方式如下:

CREATE TABLE table_name (
    column1 datatype PRIMARY KEY,
    column2 datatype,
    ...
);

 唯一索引

保证索引列中的所有值都是唯一的,但允许有空值。在业务有唯一性的情况下,我们就可以对字段建立唯一索引,因为区分度很高。

ALTER TABLE table_name ADD UNIQUE (column1);

普通索引

最基本的索引类型,没有唯一性的要求。 

CREATE INDEX index_name ON table_name (column1);

前缀索引

只对字符串类型的列的前几个字符创建索引,可以节省空间。在对于uuid查找的时候我们就可以建立前缀索引调高查询的效率和节约索引使用空间。

CREATE INDEX index_name ON table_name (column1(10));

三、B+Tree原理

上面我们已经看过B+Tree的结构了,如下所示:

B+树(B+ Tree)是一种自平衡的树数据结构,它是B树的一种变体,广泛用于数据库和操作系统的文件系统中。B+树的设计使得它特别适合用于存储、检索和维护大型数据集,尤其是在磁盘存储上。以下是B+树的一些关键特点:

  1. 节点结构:B+树的每个节点包含多个键值和多个指针,这些指针指向子节点。在B+树中,所有的数据记录节点都是按顺序存放在叶子节点中,而非叶子节点仅存储键值信息。

  2. 有序性:B+树中的键值是有序的,这使得它可以进行范围查询和顺序访问。

  3. 平衡性:B+树通过保持所有叶子节点在相同层级上,确保了树的平衡性,从而保证了操作的效率。

  4. 磁盘I/O优化:由于B+树的叶子节点都在同一层,并且叶子节点之间通过指针相连,这使得B+树在磁盘I/O操作上更加高效。因为访问叶子节点的数据不需要回溯到非叶子节点,减少了磁盘访问次数。

  5. 查询性能:B+树支持高效的查找、插入、删除和顺序访问操作。由于数据记录节点都集中在叶子节点,并且叶子节点之间是相连的,所以B+树特别适合执行范围查询和顺序扫描。

  6. 空间效率:B+树的非叶子节点不存储数据记录,只存储键值和指针,这使得B+树在存储空间上更加高效。

  7. 动态调整:B+树可以根据数据的增加或减少动态调整树的高度和节点的分裂与合并,以保持树的平衡。

四、索引失效场景

不满足最左匹配原则:在使用联合索引时,如果查询条件不满足最左匹配原则,即查询没有从联合索引的最左边列开始,索引将不会生效。

-- 假设存在(id, name)的联合索引
SELECT * FROM users WHERE name = 'John';

使用了SELECT *:当使用SELECT *来查询所有列时,可能会阻止使用覆盖索引,因为覆盖索引只需要读取索引中的列数据,而不是全行数据。

SELECT * FROM users WHERE id = 1;

索引列参与运算:如果在WHERE子句中对索引列进行运算或计算,如age + 1 = 7,索引将失效。

SELECT * FROM users WHERE age + 1 = 20;

索引列使用了函数:对索引列使用函数,如SUBSTRCONCAT,会导致索引失效。

SELECT * FROM users WHERE SUBSTR(username, 1, 3) = 'Joh';

错误的LIKE使用:在使用LIKE进行模糊查询时,如果通配符%在参数的左侧,如LIKE '%value',索引将失效。

SELECT * FROM users WHERE username LIKE '%Doe';

类型隐式转换:如果查询条件中的数据类型与索引列的类型不匹配,如将字符串类型的索引列与数字进行比较,索引可能失效。

SELECT * FROM users WHERE age = '30';

使用OR关键字:如果使用OR连接多个条件,但不是所有条件都使用索引列,可能会导致索引失效。

SELECT * FROM users WHERE age = 10 OR username = 'John';

两列做比较:在WHERE子句中比较两个索引列,如id = age,可能会导致索引失效。

SELECT * FROM users WHERE age = id;

不等于比较:使用!=<>进行比较时,可能会导致索引失效,尤其是当结果集占比较大时。

SELECT * FROM users WHERE age != 20;

IS NOT NULL:使用IS NOT NULL作为查询条件可能会导致索引失效。

SELECT * FROM users WHERE age IS NOT NULL;

NOT INNOT EXISTS:这些条件可能会导致索引失效,尤其是当NOT IN用于非主键列时。

ORDER BY导致索引失效:如果ORDER BY子句中的列不是索引列,或者排序的列顺序与索引列顺序不一致,可能会导致索引失效,存在order by的sql语句中一定要吧orderby中用到的参数放到最后一个。

//索引必须要是(age,username),(username,age)导致索引失效
SELECT * FROM users where age = 20 ORDER BY username;

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

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

相关文章

【mysql】SQL语言的概述

基本select语句的使用&#xff1a; SQL概述&#xff1a; 1946年&#xff0c;第一台电脑诞生了&#xff0c;有很多基础技术在里面&#xff0c;但是在几十年来&#xff0c;保留下来的技术SQL就是其中之一&#xff0c; 1974年的时候&#xff0c;IBM的研究员发布了一篇揭开数据库…

代码随想录算法训练营第35天|背包问题基础、46. 携带研究材料(01背包二维解法)(01背包一维解法)(acm)、416. 分割等和子集

目录 0、背包问题基础01背包 46. 携带研究材料&#xff08;01背包&#xff09;1、题目描述2、思路3、code&#xff08;二维解法&#xff09;3-1、code&#xff08;一维解法&#xff09;4、复杂度分析 416. 分割等和子集1、题目描述2、思路3、code4、复杂度分析 0、背包问题基础…

如何有效地管理个人时间:策略与技巧

在快节奏的生活环境中&#xff0c;时间成为了最宝贵的资源之一。有效地管理时间不仅能提高工作效率&#xff0c;还能提升生活质量&#xff0c;使你有更多的时间投入到家庭、爱好和个人成长中去。本文将分享一些实用的时间管理技巧&#xff0c;帮助你更好地安排每日事务。 时间…

[HZNUCTF 2023 preliminary]easyAPK-快坚持不下去的第三天

第一做安卓题&#xff0c;前提jadx,java环境&#xff0c;模拟器&#xff0c;我配了好久&#xff0c; 这段代码实现了一个简单的登录界面&#xff0c;用户需要输入用户名和密码。用户名和密码会与预设的硬编码值进行比较&#xff0c;登录成功后会启动另一个 Activity。如果密码错…

TS 学习(一)

如果我们在 ts 中写 不用运行就能在文件中报错 ts 是一种静态类型的检查 能将运行时出现的错误前置 一般不用 命令行编译 ts 转换成 js 将中文转码 tsc index&#xff08;.ts&#xff09; 输入命令生成 配置文件 能在中间进行 配置转换成 js 的哪个规范 es5 还是 6 和其它转…

鸿蒙(API 12 Beta6版)图形【过度绘制调试使用指导】方舟2D图形服务

当应用页面布局的嵌套程度过深时&#xff0c;应用渲染阶段会存在一些组件的绘制指令被其他组件的绘制指令部分或完全覆盖遮挡的情况&#xff0c;造成冗余的cpu、gpu等计算资源的使用。这种一个屏幕上的像素点被重复绘制了多次的情况被称为过度绘制&#xff08;Overdraw&#xf…

Vite - 兼容旧版浏览器 plugin-legacy(2)

目录 1&#xff0c;问题2&#xff0c;解决3&#xff0c;String 其他新增 API 的版本 接上文 Vite - 兼容旧版浏览器 plugin-legacy&#xff08;1&#xff09; 1&#xff0c;问题 客户浏览器报错&#xff0c;不支持 replaceAll 方法。 该方法在 query-string 依赖内部使用了。…

通过生日计算年龄

// 获取当前月 function getDate(date) {let d new Date(date);// 将日期设置为下月一号d.setMonth(d.getMonth() 1);d.setDate(1);// 获取本月最后一天d.setDate(d.getDate() - 1);return d.getDate(); } // 获取年龄 传出生日期和当前日期&#xff0c;当前日期可以不用传 f…

双指针(1)_数组分块_移动零问题

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 双指针(1)_数组分块_移动零问题 收录于专栏【经典算法练习】 本专栏旨在分享学习C的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1.…

ELK学习笔记(二)——使用K8S部署Kibana8.15.0

上篇文章我们完成了&#xff0c;ES的集群部署&#xff0c;如果还没有看过上篇文章的兄弟&#xff0c;可以去看看。 ELK学习笔记&#xff08;一&#xff09;——使用K8S部署ElasticSearch8.15.0集群 话不多说&#xff0c;接下来直接进入kibana的搭建 一、下载镜像 #1、下载官方…

hackme靶机通关攻略

1、登录靶机&#xff0c;查询是否有注入点 2、判断闭合方式 输入OSINT and 11 # 输入OSINT and 12 # 得出闭合方式为单引号 2、查询数据库名 输入-1 union select database(),2,3 # 3、查询数据库中的表 输入-1 union select group_concat(table_name),2,3 from informa…

搜维尔科技:数据手套+机械手遥操作,五指触感灵巧手解决方案!

本灵巧手模仿人手的自由度分配及相关运动学参数进行设计的灵巧手&#xff0c;整手的手指设计采用了模块化的思想。整机重量轻&#xff0c;单指指尖抓取力强&#xff0c;五指灵巧手作为人体机器人或者机械臂末端操作工具&#xff0c;在工业生产的特殊环境中&#xff0c;对复杂形…

Spring事务(2)——@Transaction详解

目录 一、rollbackFor 结论&#xff1a; 二、事务隔离级别 1、MySQL 事务隔离级别&#xff08;回顾&#xff09; 2、Spring 事务隔离级别 三、Spring 事务传播机制 1、什么是事务传播机制&#xff1f; 2、事务的传播机制有哪些&#xff1f; 3、Spring 事务传播机制使用…

基于SpringBoot技术的家具网站设计与实现

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot框架 工具&#xff1a;Eclipse、Navicat 系统展示 首页 家具详情界面 购物车界面 订单…

Python 数据分析— Pandas 基本操作(上)

文章目录 学习内容&#xff1a;一、Series 创建及操作二、DataFram 的创建与操作三、Pandas 的读写操作四、选择指定行列数据 学习内容&#xff1a; 一、Series 创建及操作 ** Series : Pandas 的一种数据结构形式&#xff0c;可理解为含有索引的一维数组。** **&#xff08;…

Python | Leetcode Python题解之第390题消除游戏

题目&#xff1a; 题解&#xff1a; class Solution:def lastRemaining(self, n: int) -> int:a1 1k, cnt, step 0, n, 1while cnt > 1:if k % 2 0: # 正向a1 stepelse: # 反向if cnt % 2:a1 stepk 1cnt >> 1step << 1return a1

【IPV6从入门到起飞】2-1 获取你的IPV6(手机、CPE等)

【IPV6从入门到起飞】2-1 获取你的IPV6&#xff08;手机、CPE等&#xff09; 1 IPV6就在身边2 手机IPV62.1 查看IPV62.2 IPV6 ping包测试2.3 IPV6入站测试 3 电脑通过CPE获取IPV63.1 拉不起宽带的打工人3.2 开始部署IPV6环境3.2.1 刷系统3.2.2 激活IPV63.2.3 设置防火墙入站&am…

Opencv实现提取卡号(数字识别)

直接开始 实行方法 解析命令行参数&#xff1a;使用argparse库来解析命令行输入&#xff0c;确保用户提供了输入图像和模板图像的路径。 读取模板图像&#xff1a;使用cv2.imread()函数读取模板图像的路径&#xff0c;并显示原始图像。 图像预处理&#xff1a; 将图像转换为…

ARM——结构体系(处理器工作模式,CPSR,立即数,汇编语言函数调用)

1、处理器工作模式 ARM有7个基本工作模式: User:非特权模式&#xff0c;大部分任务执行在这种模式FIQ:当一个高优先级(fast)中断产生时将会进入这种模式 IRQ:当一个低优先级(normal)中断产生时将会进入这种模式 Supervisor:当复位或软中断指令执行时将会进入这种模式 Abort:当存…

CAN总线数据帧的帧结构

CAN总线中的信息是以固定格式的“帧”发送的&#xff0c;当总线为空闲时&#xff0c;任何已连接的节点都可以开始发送一个新的“帧”。 在一个CAN系统中&#xff0c;数据在节点之间的发送和接收主要通过四种不同类型的“帧”来执行和控制。这四种类型分别是数据帧、远程帧、错…