Mysql原理与调优-索引原理及使用

目录

 

1.绪论

2.索引原理

2.1 索引采用的数据结构

2.1.1 B树

1.什么是B树

2.B树的优缺点

2.1.2 B+树

1.什么是B+树

3.B+树的优缺点

2.2.3 hash

2.2.4 总结

2.2 InnoDB数据存储结构(聚簇索引)

2.2.1 自底向上探寻索引

3.2.2 查询一条数据的完整流程

3.2.3 聚簇索引

2.4 MyIsam数据存储结构(非聚簇索引)

2.3 二级索引

2.4 联合索引

2.6 索引的注意点

2.7 索引的代价

3.索引的使用

3.1 索引生效失效场景分析

3.1.1 数据准备

3.1.2 等值匹配

1. 索引列和where条件后面列相等

2. 索引列和where条件后面列部分相等且遵循最左匹配原则

3. 索引列和where条件后面列部分相等但不遵循最左匹配原则

3.1.3 模糊匹配

1. 匹配列前缀

2. 非列前缀模糊匹配

3.1.4. 范围查询

1. 索引最左列进行范围查询

2. 索引非左列进行范围查询

3. 索引列范围查询,并且左侧列等值查询

3.1.5 排序

1. 按照索引列顺序排序

2. 排序列在索引里,但未按照索引顺序

3. 索引列排序,左侧列等值比较

4. ASC和DESC混用

3.1.5 分组

3.2 回表与索引覆盖

3.2.1 回表

1.什么是回表

2.回表的代价

3.2.2 索引覆盖

3.3. 怎么建立索引

3.3.1 尽量只为搜索、排序、分组字段建立索引

3.3.2 尽量为基数较大的列建立索引

3.3.3 索引列的类型尽量足够小

3.3.4 对于字符串,使用字符串前缀建立索引

3.3.5 不要让索引列出现在表达式中

3.3.5 主键尽量递增

3.3.6 减少冗余索引和重复索引

3.4 is null和使用or会走索引吗?

3.4.1 null值在索引中的处理

3.4.2 扫描区间

3.总结


 

1.绪论

索引可以说是Mysql或者关系型数据库中最重要的部分之一,而通过建立合适的索引来提升查询效率也是我们在日常开发过程中优化的最常用的手段。本文主要讲解索引的原理,以及如何使用索引来提升查询效率。

2.索引原理

2.1 索引采用的数据结构

2.1.1 B树

1.什么是B树

f132710d610349f6aeafc5923e15c79c.png

如图所示,B树满足如下特征:

1.B树种的每条数据为一个[key,value]的形式,并且每个节点可以存储多条数据;

2.B树每个节点内元素按照key从小到大排列;

3.对于B树的分支节点,每个节点的key值构成多个范围,比如上图父节点将区间分为<6,[6,20),[20,108),>=108,而他们的所对应的子节点key的范围应该介于上述区间内。

2.B树的优缺点

1)优点:可以将热点数据存储在靠近根节点的位置,加快索引速度。

2)缺点:B树每个节点不仅要存储的key还要存储数据,导致每个节点存储key值变少,让整棵B树变高。

4)使用场景:B树主要用于文件系统或者部分数据索引,比如MogoDB,但是大部分关系型数据库还是采用B+树作为索引。

2.1.2 B+树

1.什么是B+树

2cad3332cb6d499fb98735474cc95243.png

如图所示,B+树有如下特点:

1.B+树有叶子节点和非叶子节点之分,非叶子节点值存储key值,非叶子节点存储key值和value值。

2.和B树一样,key值大小从左到右按照从小到大排列,并且子节点的key值一定在父节点的区间范围内。

3.叶子节点之间采用指针连接,构成一个双向链表。

3.B+树的优缺点

1.B+树的非叶子节点值存储key值,每个节点能够存储更多的key值,整棵树的高度更低;

2.B树的数据都存储在非叶子节点上面,所以查询时间为O(H),其中H为B+树的高度,查询时间很稳定;

3.B+树每个叶子节点采用指针连接成双端链表。针对表扫描,只用读取叶子节点,扫描能力更强。

2.2.3 hash

InnoDB中还会有一种数据结构,就是hash,它底层采用hash表,同时采用链地址法来解决hash冲突。查询速度为O(1),但是它不支持排序和范围查询。

2.2.4 总结

InnoDB默认采用B+树作为索引的数据结构,但是如果某些数据页被频繁访问,InnoDB会将这些索引结构存储到hash表中,提升访问速度。这一过程是InnoDB自己完成的,我们并不能干预。这一过程称为自适应哈希,可以通过innodb_adaptive_hash_index这一参数来启停这一功能。

2.2 InnoDB数据存储结构(聚簇索引)

2.2.1 自底向上探寻索引

在索引的数据结构中,我们知道索引其实是一棵B+树,但是我们结合前面的InnoDB的页结构,来进行分析,也会得到这一索引结构。

在前面我们知道,Innodb是以一个16kb的数据页来进行磁盘IO的,每个数据页的页头有一个前向指针和后向指针,而每个数据页里面有多条记录,每个条记录按照主键从小到大排列,并且采用单向指针串成一个单向链表。这个时候其实就已经形成了B+树种的叶子节点。

8808f6c4b41e41c4a2bae204a867feba.png

在前面讲页目录的时候,为了提升查询性能,建立了页目录,目的就是为了采用二分查找查询数据。这里我们也可以采用相同的思想,可以将每页的最小的key值提取出来,将页号设置为value,将其放到一个节点上去,形成一个目录。

9d3d000f36be4cff9f5a92290c971fa9.png

可以看出,这个时候变已经得形成了一个B+树。

前面讲页目录结构的时候,说过record_type表示不同的记录,record_type=0表示普通数据页记录,record_type=1表示分支节点存储索引的记录,record_type=2表示每页的最小记录,record_type=1表示每页的最大记录。

3.2.2 查询一条数据的完整流程

1.InnoDB聚簇索引根节点位置是固定的,所以先在固定位置查询到根节点;

2.在分支节点上通过主键值在分支节点内进行范围查找,找到存储数据的叶子节点的页号;

3.在页中通过页中的页目录进行二分查找得到该条数据所处的分组;

4.在分组内进行匹配,得到对应数据。

3.2.3 聚簇索引

在上面构建数据目录的结构中,主键作为key,value为数据记录内容,构建的索引就叫聚簇索引。InnoDB就采用的聚簇索引,并且它的数据也是这样存储在磁盘中的。

2.4 MyIsam数据存储结构(非聚簇索引)

MyIsam中,数据进入的时候是按照到达顺序存储在磁盘中的,和每个叶子节点存储的是主键id和数据内容的地址,所以在查询数据的时候,就算是走主键索引,也会进行回表。这就是非聚簇索引。

2.3 二级索引

上面说了InnoDB数据其实是以主键索引的形式存储的,但是如果我们想要根据某个自定义的列建立索引,该怎么办。这就需要建立二级索引。二级索引其实就是以自定义列作为key,主键id作为value。

2059e156cdef4bb2ae11fba40ca28668.png

2.4 联合索引

我们也可以根据多列建立索引,比如key(a,b)建立联合索引,首先按照a列进行排序,如果a列相当在按照b列进行排序。

2.6 索引的注意点

1.聚簇索引根节点的存储位置是固定的;

2.在建立二级索引时,为了保证二级索引,目录项的唯一性,会将id冗余到二级索引的非叶子节点上。并为了进行回表,也会将主键id冗余到叶子节点上。

3.为了保证B+树的扁平化,每个非叶子节点至少存储两条记录。

2.7 索引的代价

1.索引会浪费空间,因为需要额外的空间来存储索引结构;

2.索引会浪费时间,在我们在插入数据的时候,需要多维护一种数据结构;

综上,索引适合于查多写少的场景。

3.索引的使用

3.1 索引生效失效场景分析

3.1.1 数据准备

CREATE TABLE person_info(id INT NOT NULL auto_increment,name VARCHAR(100) NOT NULL,birthday DATE NOT NULL,phone_number CHAR(11) NOT NULL,country varchar(100) NOT NULL,PRIMARY KEY (id),KEY idx_name_birthday_phone_number (name, birthday, phone_number)
);

本文将以上述结构建立联合索引,所以这个索引的列,是按照先按照name进行排序,然后按照birthday顺序排序,最后按照phonenumber进行排序的。

3.1.2 等值匹配

1. 索引列和where条件后面列相等

where条件后面的列刚好是在索引列

select * from person_info where `name`='ello' AND birthday ='1998-07-01' and phone_number = 12321312321

对于联合索引的等值匹配,这个时候会直接走索引,先匹配name匹配到过后再在b+树种查找birthday ='1998-07-01'然后再查找country = 'china'的内容。等值匹配的顺序是可以变的,不管是他们顺序是什么样的,都会被优化成索引的顺序,先匹配name,再匹配birthday,再匹配phonenumber。

2. 索引列和where条件后面列部分相等且遵循最左匹配原则

where条件后面的列,有name和birthday这两列,是按照索引的建立顺序从左能够匹配上的,这称之为最左匹配原则。

SELECT * FROM person_info WHERE `name`='ello' AND birthday ='1998-07-01' AND conutry = 'China'

这个时候,name和birthday会走二级索引获取到主键id,然后通过主键索引回表后过滤出conutry = 'China'的数据。

3. 索引列和where条件后面列部分相等但不遵循最左匹配原则

这个时候,where条件后面没有根据name进行判断,但是索引建立是name相当,才根据birthday进行排序,所以需要向把name定下来,才能对birthday采用二分查找。所以这种情况不能走索引。

SELECT * FROM person_info WHERE  birthday ='1998-07-01' AND conutry = 'China'

3.1.3 模糊匹配

1. 匹配列前缀

SELECT * FROM person_info WHERE  `name` LIKE 'zhang%'

上面name排在第一列,并且对于字符串建立的索引,可以认为也是一个聚簇索引,每次比较的时候比如'zhang'和'zhba'这两个字符串,也是从左到右一个字符一个字符的进行比较,页应该满足最左匹配原则。所以上述会走索引。

2. 非列前缀模糊匹配

SELECT * FROM person_info WHERE  `name` like '%zhang'

上述sql不满足最左匹配原则,则不走索引。

3.1.4. 范围查询

1. 索引最左列进行范围查询

SELECT * FROM person_info WHERE  `name` > 'aaa' and 'name' < 'nnn'

最左列式有序的,所以直接查询出name='aaa'的数据,然后再叶子节点上通过后向指针遍历即可。所以会走索引。

2. 索引非左列进行范围查询

SELECT * FROM person_info WHERE  phone_number > 1213123 and phone_number < 5555555

左侧列没有固定,所以在全局来看,该列是随机的,不能走索引。

3. 索引列范围查询,并且左侧列等值查询

SELECT * FROM person_info WHERE  `name`='ello' AND birthday ='1998-07-01' and phone_number > 1213123 and phone_number < 5555555

name和birthday等值查询后就已经固定下来,然后再通过phone进行范围查询,可以采用二分查找,会走索引。

3.1.5 排序

1. 按照索引列顺序排序

SELECT * FROM person_info ORDER BY  `name` ASC, birthday ASC

索引会先按照name升序排序,在按照birthday排序,最后按照phone升序排序,刚好满足sql,所以会走索引。

2. 排序列在索引里,但未按照索引顺序

SELECT * FROM person_info ORDER BY  birthday ASC

左侧name未固定,所以birthday对于整棵索引树来说是乱序,不会走索引。

3. 索引列排序,左侧列等值比较

SELECT * FROM person_info WHERE `name`='ello' ORDER BY  birthday ASC

name字段已经被固定了,所以索引树是哪找birthday进行排序的,所以会走索引。

4. ASC和DESC混用

SELECT * FROM person_info ORDER BY  `name` ASC, birthday DESC

这种情况,name可以通过索引进行排序,然后将数据读取到sort buffer中,最后通过文件排序对birthday进行排序。

3.1.5 分组

索引其实天然就带有分组的功能,比如上面的联合索引可以理解为先对name进行分组,然后再对brthday进行分组,最后对phone_number进行分组。

SELECT name, birthday, phone_number, COUNT(*) FROM person_info GROUP BY name, birthday, phone_number

所以针对上述sql,也会走索引。

3.2 回表与索引覆盖

3.2.1 回表

1.什么是回表

在二级索引中,如果查询的数据在二级索引中并没有存储,就需要通过二级索引得到主键id,然后通过主键id到主键索引中回表得到完整的记录。

比如

SELECT * FROM person_info WHERE `name`='ello' AND birthday ='1998-07-01' AND phone_number = 12321312321

我们需要根据idx_name_birthday_phone_number 这个索引查询出满足条件的id值,然后根据主键id到主键索引中获取到完整的记录。

这就是我们为什么不建议使用select *的原因,因为这样会加大回表的概率。

2.回表的代价

1.我们从索引中获取主键id的时候,因为索引是按照顺序存储的,所以这个时候基本上可以认为是顺序IO,性能较好。

2.拿到主键id过后,由于主键id可能是乱序的,然后到主键索引中查询,这个时候是随机IO,性能较差。

3.2.2 索引覆盖

我们如果让二级索引刚好包含需要查询的列,这个时候结果就只需要从二级索引中获取,不用回表到主键索引中再次获取,就称之为索引覆盖。

3.3. 怎么建立索引

3.3.1 尽量只为搜索、排序、分组字段建立索引

尽量只为搜索、排序、分组字段建立索引。当然,如果某个sql特别重要,为了减少回表,也可以为sql的将该sql的查询字段冗余到索引中,减少回表。这些都需要结合实际而定。

3.3.2 尽量为基数较大的列建立索引

比如状态类型这种列值只有几个选择的列时,如果要建立索引,由于有大量重复值,这个索引也基本上不会生效。这个时候我们可以考虑从业务角度优化,比如查询是带上时间,建立一个时间和状态的联合索引,增大索引列的基数。

3.3.3 索引列的类型尽量足够小

索引列尽量使用tinyint,int这种类型,这样b+树的每个节点会存储更多内容,减少树的高度。

3.3.4 对于字符串,使用字符串前缀建立索引

字符串比较的时候,是按照一个字符一个字符进行比较的。如果将整个字符串都作为索引的话,首先整棵索引树会特别大,比较的时候,如果索引树比较深,或者用or连接多个字符串模糊匹配的场景,优化器可能会选择全表扫描。

SELECT * FROM person_info WHERE  `name` like 'aaa%' or `name` like 'bbb%' or `name` like 'ccc%' or `name` like 'ddd%'

所以,我们可以截取字符串的部分前缀建立索引,这个时候可以减少索引树的大小。但是这样不能将其用于排序。

3.3.5 不要让索引列出现在表达式中

尽量不要在where条件后面的索引列中,进行表达式运算,这样会导致索引失效。

3.3.5 主键尽量递增

主键递增,可以减少主键索引的页分裂。

3.3.6 减少冗余索引和重复索引

维护冗余索引和重复索引会降低性能。

3.4 is null和使用or会走索引吗?

3.4.1 null值在索引中的处理

Mysql默认null值是索引中的最小值。

3.4.2 扫描区间

对于is null或者or,Mysql的优化器首先会根据写的sql得出一个扫描区间:

比如is null的扫描区间就是[null,null],is not null的扫描区间(null,+∞],而对于下面这个用sql连接的语句器扫描区间为[11111,22222] U [33333,44444] U [55555,66666]。

SELECT * FROM person_info WHERE   phone_number > 11111 and phone_number < 22222 or   phone_number > 33333 AND phone_number < 44444or   phone_number > 55555 AND phone_number < 66666

Mysql的优化器会根据索引对扫描区间进行扫描的话估算出执行代价,然后对比全表扫描的代价,判断哪种方式代价更低,然后选择索引或者全表扫描。

网上说的is null不走索引,大概率是因为表的数据量不够或者如果运行字段为空,大概率这个字段会出现大量为空的记录。所以对于null这个值,基数会很小,执行器在计算代价的时候,跟倾向于选择全表扫描。

3.总结

索引是sql优化的最主要的手段,只有掌握原理才能知道哪些场景索引会失效或者生效。对于索引失效或者生效,是由执行器根据走不同的索引或者全表扫描得到执行计划,然后估算出不同的代价比较后选择代价最小的执行计划。执行计划的选择会受很多条件影响,比如数据量,或者索引树的存储排列结构。我们只有根据索引的原理去分析是否走索引,但是因为其他因素影响,可能真正的执行结果和我们预想的不一致。这些都是要结合实际数据场景去分析的。而且优化器选择的执行计划也不一定是最优的,有时通过强制索引来使得某个索引生效,也是一种优化手段。

 

 

 

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

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

相关文章

奥运科技观察:AI PC,如何成为当代体育精神的数字捍卫者?

作者 | 曾响铃 文 | 响铃说 数字孪生帮助体育馆建设、超高清直播……这届奥运会科技感拉满&#xff0c;几乎所有前沿技术都能在奥运的赛事运营中发现。 而AI大时代&#xff0c;AI如何帮助帮助奥运会顺利举办、如何帮助运动员拥有更好的表现&#xff0c;同样值得业界关注&…

haproxy最强攻略

1、负载均衡 负载均衡&#xff08;Load Balance&#xff0c;简称 LB&#xff09;是高并发、高可用系统必不可少的关键组件&#xff0c;目标是 尽力将网络流量平均分发到多个服务器上&#xff0c;以提高系统整体的响应速度和可用性。 负载均衡的主要作用如下&#xff1a; 高并发…

# Spring Cloud Alibaba Nacos_配置中心与服务发现(四)

Spring Cloud Alibaba Nacos_配置中心与服务发现&#xff08;四&#xff09; 一、Nacos 配置管理-集群部署 1、 把 nacos 应用程序包&#xff0c;复制3份&#xff0c;分别命名为 nacos1, nacos2, nacos3 分别在 conf 目录下&#xff0c;修改 application.properties 配置文件…

数据结构——循环队列

目录 循环队列的基本知识 循环队列的实现 定义 各个接口的实现 循环队列的基本知识 循环队列的定义 循环队列&#xff08;Circular Queue&#xff09;是一种使用固定大小的数组实现的队列&#xff0c;它将数组的首尾相连&#xff0c;形成环形&#xff0c;以充分利用空间并实…

Spring Boot的配置文件

目录 一、配置文件 1.properties为后缀的配置文件 1.1基本语法 1.2读取配置文件 1.3properties的优缺点 1.4加中文注释出现乱码 2.yml格式的配置文件 2.1基础语法 2.2读取配置文件 2.2.1对象存储到配置文件中 2.3yml的优缺点 2.4用不用加单引号或者双引号呢&#xf…

【C语言篇】编译和链接以及预处理介绍(上篇)

文章目录 前言翻译环境和运行环境翻译环境编译预处理&#xff08;预编译&#xff09;编译词法分析语法分析语义分析 汇编 链接 运行环境预处理&#xff08;预编译&#xff09;详解预定义符号#define定义常量#define定义宏带有副作用的宏参数宏替换的规则宏和函数的对比 写在最后…

opencv基础的图像操作

1.读取图像&#xff0c;显示图像&#xff0c;保存图像 #图像读取、显示与保存 import numpy as np import cv2 imgcv2.imread(./src/1.jpg) #读取 cv2.imshow("img",img) #显示 cv2.imwrite("./src/2.jpg",img) #保存 cv2.waitKey(0) #让程序进入主循环(让…

RAG系列之四:深入浅出 Embedding

在 RAG 系列之三&#xff1a;文本切分中介绍了如何将文本切分成更小的语义单元&#xff0c;接下来便是将拆分的文本块进行向量化。 什么是文本向量化&#xff1f; 文本向量化就是将文本数据转成数字数据&#xff0c;例如&#xff1a;将文本 It was the best of times, it was…

Android全面解析之context机制(二): 从源码角度分析context创建流程(上)

前言 这篇文章从源码角度分析context创建流程。 在上一篇Android全面解析之Context机制(一) :初识context一文中讲解了context的相关实现类。经过前面的讨论&#xff0c;读者对于context在心中有了一定的理解。但始终觉得少点什么&#xff1a;activity是什么时候被创建的&…

Python数据可视化案例——地图

目录 简单案例&#xff1a; 进阶案例&#xff1a; 继上文数据可视化案例&#xff0c;今天学习用pyecharts练习数据可视化案例2-构建地图。 简单案例&#xff1a; 首先构建一个简单的地图。 代码&#xff1a; import json from pyecharts.charts import MapmapMap() data[…

培训学校课程管理系统-计算机毕设Java|springboot实战项目

&#x1f34a;作者&#xff1a;计算机毕设残哥 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目、 源…

大数据面试SQL(八):求连续段的起始位置和结束位置

文章目录 求连续段的起始位置和结束位置 一、题目 二、分析 三、SQL实战 四、样例数据参考 求连续段的起始位置和结束位置 一、题目 有一张表t2_id记录了id&#xff0c;id不重复&#xff0c;但是会存在间断&#xff0c;求出连续段的起始位置和结束位置。 样例数据&…

结构体structure、共用体union

目录 结构体 结构体类型的定义形式 结构体类型的大小 内存计算例子 共用体union 用共用体判断大小端 结构体和共用体对比 qsort&#xff08;&#xff09; 结构体 结构体类型——用来描述复杂数据的一种数据类型 构造类型&#xff08;用户自定义类型&#xff09; struc…

CUDA+tensorflow+python+vscode在GPU下环境安装及问题汇总与解答

2024.8.14 因为要做深度学习&#xff0c;需要安装tensorflowgpu的环境&#xff0c;每次都搞不好整的很生气&#xff0c;本次将安装过程中参考的一些大佬的博客和安装过程中遇到的问题及解决方案总结一下&#xff0c;希望以后不要在这件事情上浪费时间。安装环境其实也没有想象中…

Halcon图像平滑与去噪

Halcon图像平滑与去噪 文章目录 Halcon图像平滑与去噪1. 均值滤波2. 中值滤波3. 高斯滤波5. 光照不均匀 有时拍摄的图像中会存在很多杂点和噪声&#xff0c;对于比较均匀的噪声&#xff0c;可以考虑用软件的算法进行 消除。例如&#xff0c;可以用图像平滑的方法进行去噪&#…

uniapp 自定义全局弹窗

自定义全局弹窗可在js和.vue文件中调用&#xff0c;unipop样式不满足&#xff0c;需自定义样式。 效果图 目录结构 index.vue <template><view class"uni-popup" v-if"isShow"><view class"uni-popup__mask uni-center ani uni-cust…

数学建模——启发式算法(蚁群算法)

算法原理 蚁群算法来自于蚂蚁寻找食物过程中发现路径的行为。蚂蚁并没有视觉却可以寻找到食物&#xff0c;这得益于蚂蚁分泌的信息素&#xff0c;蚂蚁之间相互独立&#xff0c;彼此之间通过信息素进行交流&#xff0c; 从而实现群体行为。 蚁群算法的基本原理就是蚂蚁觅食的过程…

R语言的算数运算

下面内容摘录自《R 语言与数据科学的终极指南》专栏文章的部分内容&#xff0c;每篇文章都在 5000 字以上&#xff0c;质量平均分高达 94 分&#xff0c;看全文请点击下面链接&#xff1a; 3章3节&#xff1a;R的赋值操作与算术运算_r 链式赋值-CSDN博客文章浏览阅读172次。掌…

Ajax-02.Axios

Axios入门 1.引入Axios的js文件 <script src"js/axios-0.18.0.js"></script> Axios 请求方式别名: axios.get(url[,config]) axios.delete(url[,config]) axios.post(url[,data[,config]]) axios.put(url[,data[,config]]) 发送GET/POST请求 axios.get…

Windows的cmd命令行使用Linux类命令

Windows的cmd使用Linux类命令 去我的个人博客观看&#xff0c;观感更佳哦&#xff0c;&#x1f619;&#x1f619; 前言 我在使用Vscode编写C/C代码的时候&#xff0c;经常会用到Shell(你可以理解为命令行)&#xff0c;但是我不得不说Windows下Dos命令极其难用且拉跨&#x1f…