一、Mysql索引的底层数据结构与算法

Mysql索引的底层数据结构与算法

  • 前言
  • 一、索引数据结构
    • 为什么 MySQL 的索引要使用 B+ 树而不是其他树形结构?比如 B 树?
    • 为什么InnoDB存储引擎选择使用B+tree索引结构?
  • 二、索引分类
    • 思考:以下SQL语句,那个执行效率高?为什么?备注:id为主键,name字段创建的有索引,age普通字段
    • 一张表,有四个字段(id,username,password,status)由于数据量大,需要对以下SQL语句进行优化,该如何进行才是最优方案:select id,username,password from tb user where username ='itcast';
    • 思考:InnoDB主键索引的B+tree高度为多高呢?
  • 三、索引语法
    • 3.1 创建索引
    • 3.2 查看和删除索引
    • 3.3 索引提示
  • 聚集索引&聚簇索引&稀疏索引到底是什么
  • mysql为什么建议使用自增主键
    • 3.1 业务层面
    • 3.2 性能层面
  • 联合索引底层数据结构又是怎样的
  • Mysql最左前缀优化原则是怎么回事

前言

索引(index)是帮助MySQL高效获取数据的一种有序数据结构。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。

优势:

  • 提高数据检索的效率,降低数据库的成本,减少io交互
  • 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗。
    劣势:
  • 索引列也是要占用空间的。
  • 索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行INSERT、UPDATE、DELETE时,效率降低。

一、索引数据结构

数据结构学习网址

  1. 索引为什么不使用二叉树作为索引结构?
    二叉树缺点:顺序插入时,会形成一个链表,查询性能大大降低。大数据量情况下,层级较深,检索速度慢。如下图
    在这里插入图片描述

我们如果查询5这个值时,其查询了4次,这个还是数据比较少,如果数据比较多,那么就会形成很深的层级,查询性能大大降低。

  1. 那红黑树呢?
    在这里插入图片描述
    看起来,层级变少了,查询5这个值只用了2步,但是红黑树也是一种二叉树,在数据比较多,也会形成很深的层级,查询性能也会较低,只是比二叉树好点

看来层级越少,查询性能越好,那有没有什么数据结构,在大数据情况下,层次也很少呢。有,下面开始介绍B-Tree

  1. BTree又称多路平衡查找树,叶节点具有相同的深度,叶节点的指针为空所有索引元素不重复,节点中的数据索引从左到右递增排列
    在这里插入图片描述
    B+Tree,非叶子节点不存储data,只存储索引(冗余),可以放更多的索引叶子节点包含所有索引字段,叶子节点用指针连接,提高区间访问的性能

以一颗最大度数(max-degree)为4(4阶)的b+tree为例:

为什么 MySQL 的索引要使用 B+ 树而不是其他树形结构?比如 B 树?

因为 B 树不管叶子节点还是非叶子节点,都会保存数据,这样导致在非叶子节点中能保存的指针数量变少(有些资料也称为扇出)。
指针少的情况下要保存大量数据,只能增加树的高度,导致 IO 操作变多,查询性能变低

为什么InnoDB存储引擎选择使用B+tree索引结构?

相对于二叉树,层级更少,搜索效率高;
对于B-tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低;
相对Hash索引,B+tree支持范围匹配及排序操作;

二、索引分类

在这里插入图片描述
在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:
在这里插入图片描述
聚集索引选取规则:
如果存在主键,主键索引就是聚集索引。
如果不存在主键,将使用第一个唯一(unique)索引作为聚集索引。
如果表没有主键,或没有合适的唯一索引,则nnoDB会自动生成一个rowid作为隐藏的聚集索引。

聚集索引结构图如下
在这里插入图片描述
非聚集索引结构图如下
在这里插入图片描述

思考:以下SQL语句,那个执行效率高?为什么?备注:id为主键,name字段创建的有索引,age普通字段

select *  from user where id =10;
select * from user where name ='Arm';
select id,name from user where name ='Arm';
select id,name,age from user where name ='Arm';

第一个SQL:id是聚集索引,他查到10这个节点时,就可以直接获取他的row
第二个SQL:name是非聚集索引,他查到Arm这个节点时,他不会直接获取他的row,而是获取他的聚集索引,如10,然后通过其聚集索引值获取他的row
第三个SQL:因为name是非聚集索引,他查到Arm这个叶子节点时,可以直接获取id,不会去继续回表查询
第四个SQL:当查到Arm这个叶子节点时,可以直接获取id,但是获取不到age的值,所以他需要通过id,继续回表查询

一张表,有四个字段(id,username,password,status)由于数据量大,需要对以下SQL语句进行优化,该如何进行才是最优方案:select id,username,password from tb user where username =‘itcast’;

对username和password创建联合索引,如果只对username创建索引,那么会产生回表查询
在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引,而非单列索引。

思考:InnoDB主键索引的B+tree高度为多高呢?

在计算机中磁盘存储数据最小单元是扇区,一个扇区的大小是512字节,而文件系统(例如XFS/EXT4)他的最小单元是块,一个块的大小是4k,而对于我们的InnoDB存储引擎也有自己的最小储存单元——页(Page),一个页的大小是16K

假设:一行数据大小为1k,一页中可以存储16行这样的数据。InnoDB的指针占用6个字节的空间,主键即使为oigint,占用字节数为8。

高度为2:

n8+(n+1)6=161024,算出n约为1170
1171
16=18736
高度为3:

1171117116=21939856 (约 2 千万)

三、索引语法

3.1 创建索引

如果只关联一个字段,那么就是单列索引,如果关联多个字段,那么就是组合索引‘

create unique index 索引名 on 表名(字段名,字段名,字段名)//创建唯一索引  

create fulltext index 索引名 on 表名(字段名,字段名,字段名)//创建全文索引 

create  index 索引名 on 表名(字段名,字段名,字段名)//创建普通索引 

当字段类型为字符串(varchar,text等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘io,影响查
询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。

create index 索引名 on 表名(字段名(n); //创建前缀索引

前缀长度n,可以根据索引的选择性来决定,而选择性是指杯重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,最高为1,这是最好的索引选择性,性能也是最好的。下面以字段email为例

select count(distinct substring(email,1,5))/count(*)from tb_user

3.2 查看和删除索引

show index from 表名 #查看索引

drop index  索引名 on 表名 #删除索引

3.3 索引提示

索引提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。

在Mysql如果一个字段上存在1个以上的索引,那么Mysql会自己选一个索引生效

create index school_home_index on person(school,home)

create index school_index on person(school)

explain select * from person where  school = '海'

在这里插入图片描述
如果我们自己想指定索引生效,那么可以使用下面的语法

#建议使用索引school_index,所以有可能不用
select  *from person use index(school_index)where school = '海'

#忽略索引school_index
select * from person user ignore index(school_index)where  school = '海'

#强制使用索引school_index
select * from person user force index(school_index)where  school = '海'

聚集索引&聚簇索引&稀疏索引到底是什么

聚集索引(Clustered Index)、聚簇索引(Cluster Index)、稀疏索引(Sparse Index)是数据库中常见的索引类型。

  1. 聚集索引(Clustered Index)

    • 聚集索引是一种索引类型,它改变了数据在磁盘上的排列方式,使得数据行的物理顺序与索引键的逻辑顺序相匹配。换句话说,聚集索引中数据行按照索引键的顺序进行存储
    • 聚集索引只能有一个,因为它决定了数据行在磁盘上的排列顺序。如果表中已经有聚集索引,再创建新的聚集索引会导致表的重组,代价较高。
    • 聚集索引通常应用于经常需要范围查询(Range Query)或者按顺序获取结果的列上
  2. 非聚集索引(Non-clustered Index)

    • 非聚集索引与数据行的物理顺序无关,它将索引键与数据行的引用映射到一个独立的数据结构中。这使得非聚集索引的创建和维护成本较低。
    • 一个表可以有多个非聚集索引,因为它们不会改变数据行的物理顺序。
    • 非聚集索引通常用于经常需要单一值查找(Single Value Lookup)或者经常需要在列上进行排序的情况。
  3. 稀疏索引(Sparse Index)

    • 稀疏索引是一种优化索引结构,它仅记录部分数据行的索引信息,而不是每一行都有对应的索引条目。
    • 稀疏索引通常应用于数据分布不均匀,但又需要索引支持的列。通过只记录部分数据行的索引信息,可以节省索引占用的空间,同时保持索引的查询效率。
    • 稀疏索引在一些特定的数据库引擎中才会使用,例如 SQL Server 中的稀疏索引用于处理稀疏列(Sparse Column)。

总的来说,聚集索引、非聚集索引和稀疏索引是数据库中用于提高数据访问效率的重要工具,它们各自适用于不同的场景和需求。

mysql为什么建议使用自增主键

MySQL建议使用自增主键的原因有以下几点:

  1. 性能优化:自增主键是按顺序递增的,插入新记录时不需要进行排序操作,可以提高插入数据的速度。另外,自增主键也有利于索引的建立和查询优化。

  2. 唯一性:自增主键保证了每条记录的唯一性,避免了数据重复或冲突的情况。

  3. 简化数据维护:使用自增主键可以简化数据的维护和管理,方便进行数据的增删改查操作。

  4. 节省存储空间:自增主键通常是整型数据类型,占用的存储空间较小,可以节省数据库存储空间。

综上所述,使用自增主键可以提高数据库的性能、保证数据的唯一性、简化数据维护操作,并节省存储空间,因此在实际应用中建议使用自增主键。

3.1 业务层面

先说业务层面。

假设某个业务中的用户账号使用了自增 ID 做主键,你注意一下,我这里说的例子使用了自增 ID,但是这个 ID 被赋予了业务含义。

这个业务一直都运行的好好的。

突然有一天,公司拓展了新业务。

公司要求这个账号在新老业务上的权益不能相同,在新业务上从 0 开始计算。

根据这个需求,那么此时如果改造数据表的话,用户 ID 就不能是唯一的了,业务对唯一约束的要求变成了:account_id + business_id。

如何才能使用最小的成本,来达到业务改造的目的?

当然你可以说我们有许多方案可以用,比如新业务独立使用一张新表等等。

我们暂且不讨论其他的方案,如果当初我们没有赋予这个自增主键具体的业务含义的话,是不是问题就好解决了?

假设 account_id 只是一个唯一索引,那么我们在改造业务的时候,只需要把这个唯一索引变更为 account_id + business_id,就可以极大的减轻业务改造的成本。

因此在一般场景下,我推荐你使用无业务含义的自增 ID 作为主键。

3.2 性能层面

下面再从性能层面分析一下,这里我们使用一个生活场景来举例。

这样的场景在互联网公司中非常常见,比如购物、打车、支付等等。

这类业务有一个重要特征就是流水业务,数据热点非常集中,一般用户高频访问的数据就是最近一天、一周或一个月内。

现在我们假设你在 XX APP 看到购书满 100-50 的优惠券,仅限今天使用,然后你去抢了一张。

抢优惠券的过程分以下几步:

你点击抢券
系统在优惠券表中读取你是否有此优惠券
如果有,系统提示已领券
如果没有,查看库存
如果有库存,库存 - 1 并把领券信息写入优惠券表
在这个过程中,系统要反复读取和更新优惠券表。

如果这个表使用了自增 ID 并且数据按时间顺序递增,那么数据热点就集中在今天这个范围内。

那使用无业务含义的自增 ID 做主键会有什么好处呢?

读和更新都集中在一个范围内。

此时不管是 SELECT 还是 INSERT、UPDATE 语句都集中在表的尾部。

前面我们提到了 MySQL 有预读和刷新邻接页的特性,因此这部分热点数据的写入性能会相对较好,也能很好的缓存到 InnoDB buffer pool 中。

假设你使用了带有业务含义的主键,那插入的数据可能就会随机分布到表的各个位置,从而带来大量的随机读和刷脏页操作,拖累整个表的性能。

你可能会质疑说使用无业务含义的自增主键,会导致读取和更新时都需要走二级索引,而二级索引回表操作会有两个 IO。

如果只看单个语句的情况下确实是这样,但是互联网业务通常都是高并发的场景,我们分析问题要从点考虑到面。

通过使用自增 ID 这个特性,我们可以将活跃的数据集中到表的尾部,通过预读的特性将这条数据邻接的其他数据也 load 到 buffer pool。

那么日期比较靠近今天的优惠券信息就能缓存到 buffer pool 中,因此这部分热点数据的性能会维持在一个较佳的状态。

虽然二级索引回表会多出 1 个 IO,但是由于 buffer pool 的介入,整体看来 IO 的总量还是变少了。

假设没有使用自增 ID,我们的热点数据是离散分布在整个表中,首先 INSERT 是完全随机的,然后把热点数据全部缓存到 buffer pool 中也需要耗费更长的时间和更多的 IO,这会带来更多的性能下降。

因此我还是建议你使用无业务含义的自增 ID 作为主键。

联合索引底层数据结构又是怎样的

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Mysql最左前缀优化原则是怎么回事

因为索引就是按着顺序建成的

MySQL最左前缀优化原则是指在使用索引进行查询时,只有索引的最左边的列被使用,才能充分利用索引的优势。换句话说,如果一个索引是由多列组成的,那么只有查询条件中涉及到索引的最左边的列,索引才会被用到。

举个例子,假设有一个索引包含两列 (col1, col2),如果查询条件是 WHERE col1 = 'value' AND col2 = 'value',那么这个索引就能被充分利用。但如果查询条件是 WHERE col2 = 'value',那么这个索引就无法被利用,因为查询条件没有涉及到索引的最左边的列。

因此,在设计数据库表结构和索引时,需要考虑到最左前缀优化原则,尽量将经常用于查询的列放在索引的最左边,以提高查询性能。

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

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

相关文章

Stable Diffusion AI绘画

我们今天来了解一下最近很火的SD模型 ✨在人工智能领域,生成模型一直是研究的热点之一。随着深度学习技术的飞速发展,一种名为Stable Diffusion的新型生成模型引起了广泛关注。Stable Diffusion是一种基于概率的生成模型,它可以学习数据的潜…

数据仓库实验三:分类规则挖掘实验

目录 一、实验目的二、实验内容和要求三、实验步骤1、创建数据库和表2、决策树分类规则挖掘(1)新建一个 Analysis Services 项目 jueceshu(2)建立数据源视图(3)建立挖掘结构 DST.dmm(4&#xff…

Qt模型视图代理之QTableView应用的简单介绍

往期回顾 Qt绘图与图形视图之绘制带三角形箭头的窗口的简单介绍-CSDN博客 Qt绘图与图形视图之Graphics View坐标系的简单介绍-CSDN博客 Qt模型视图代理之MVD(模型-视图-代理)概念的简单介绍-CSDN博客 Qt模型视图代理之QTableView应用的简单介绍 一、最终效果 二、设计思路 这里…

【Android学习】日期和时间选择对话框

实现功能 实现日期和时间选择的对话框&#xff0c;具体效果可看下图(以日期为例) 具体代码 1 日期对话框 1.1 xml <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android&quo…

EPAI手绘建模APP资源管理和模型编辑器2

g) 矩形 图 26模型编辑器-矩形 i. 修改矩形的中心位置。 ii. 修改矩形的长度和宽度。 h) 正多边形 图 27模型编辑器-内接正多边形 图 28模型编辑器-外切正多边形 i. 修改正多边形的中心位置。 ii. 修改正多边形中心距离端点的长度。 iii. 修改正多边形的阶数。阶数为3&…

排序算法之堆排序

首先在了解堆排序之前我们先来回顾一下什么叫做堆吧&#xff01; 基本概念 堆&#xff08;Heap&#xff09;&#xff1a;是一种特殊的完全二叉树&#xff0c;其中每个节点的值都大于或等于&#xff08;大顶堆&#xff09;或小于或等于&#xff08;小顶堆&#xff09;其子节点的…

活动图与状态图:UML中流程图的精细化表达——专业解析系统动态性与状态变迁

流程图是一种通用的图形表示法&#xff0c;用以展示步骤、决策和循环等流程控制结构。它通常用于描述算法、程序执行流程或业务过程&#xff0c;关注于任务的顺序执行。流程图强调顺序、分支和循环&#xff0c;适用于详细说明具体的处理步骤&#xff0c;图形符号相对基础和通用…

ubuntu搭建kms服务器

1.下载kms开源包(如果提示找不到wget命令的话:apt install wget): wget https://github.com/Wind4/vlmcsd/releases/download/svn1111/binaries.tar.gz2.解压: tar -xzvf binaries.tar.gz接着cd 进入 Linux/intel/static/ 文件夹下: 3.选择对应的文件&#xff0c;这里我们选…

onedrive下載zip檔案有20G限制,如何解決

一般來說&#xff0c;OneDrive網頁版對文件下載大小的限制如下圖所示&#xff0c;更多資訊&#xff0c;請您參考這篇文章&#xff1a;OneDrive 和 SharePoint 中的限制 - Microsoft Support 因此我們推薦您使用OneDrive同步用戶端來同步到本地電腦&#xff0c;您也可以選擇只同…

C语言——rand函数

一、rand函数 这是一个在 C 标准库 <stdlib.h> 中定义的函数&#xff0c;用于生成伪随机数&#xff0c;默认情况下&#xff0c;它生成从 0 到 RAND_MAX 的伪随机数&#xff0c;其中 RAND_MAX 是一个常数&#xff0c;通常是 32767。 1、函数原型&#xff1a; 2、函数返回…

C#中.net8WebApi加密解密

尤其在公网之中&#xff0c;数据的安全及其的重要&#xff0c;除过我们使用jwt之外&#xff0c;还可以对传送的数据进行加密&#xff0c;就算别人使用抓包工具&#xff0c;抓到数据&#xff0c;一时半会儿也解密不了数据&#xff0c;当然&#xff0c;加密也影响了效率&#xff…

【Qt问题】VS2019 Qt win32项目如何添加x64编译方式

解决办法&#xff1a; 注意改为x64版本以后&#xff0c;要记得在项目属性里&#xff0c;修改Qt Settings、对应的链接include、lib等 参考文章 VS2019 Qt win32项目如何添加x64编译方式_vs2019没有x64-CSDN博客 有用的知识又增加了~

www.fastssh.com SSH over WebSockets with CDNs

https://www.fastssh.com/page/create-ssh-cdn-websocket/server/这其实不是标准的websocket报文(服务器响应报文无Sec-Websocket-Accept字段)&#xff0c;所以无法使用github.com/gorilla/websocket包&#xff1a;GET / HTTP/1.1 Host: hostname:8080 User-Agent: Go-http-cli…

43 单例模式

目录 1.什么是单例模式 2.什么是设计模式 3.特点 4.饿汉和懒汉 5.峨汉实现单例 6.懒汉实现单例 7.懒汉实现单例&#xff08;线程安全&#xff09; 8.STL容器是否线程安全 9.智能指针是否线程安全 10.其他常见的锁 11.读者写者问题 1. 什么是单例模式 单例模式是一种经典的&a…

243 基于matlab的模糊C均值算法(FCM)及其改进算法将空间邻域项引入FCM的目标函数(FCM_S)

基于matlab的模糊C均值算法&#xff08;FCM&#xff09;及其改进算法将空间邻域项引入FCM的目标函数(FCM_S),广义的模糊C均值(GFCM)算法&#xff0c;基于核的改进的模糊c均值聚类算法&#xff08;KFCM&#xff09;,基于核的广义模糊c均值聚类算法KGFCM的图像分割方法。程序已调…

一文了解python机器学习Sklearn

1.3 安装和配置Sklearn 要使用Sklearn库&#xff0c;首先需要安装Python和相应的库。在本教程中&#xff0c;我们将使用Python 3.x版本。可以使用以下命令安装Sklearn库&#xff1a; pip install scikit-learn安装完成后&#xff0c;可以在Python代码中导入Sklearn库&#xf…

【Android学习】自定义文本框和输入监听

实现功能 以上代码可实现功能&#xff1a; 1 自定义文本框样式 2. 文本框触发形式转变 3. 文本框输入长度监听&#xff0c;达到最大长度关闭软键盘 4. password框触发检测phone框内容 1. drawable自定义形状 我创建了editor_focus.xml 和 editor_unfocus.xml&#xff0c;两者仅…

猿人学第七题-动态字体-随风漂移

前言&#xff1a;该题主要是考对fontTools.ttLib.TTFont的操作&#xff0c;另外就是对字典互相映射的操作 一、woff文件存储 from fontTools.ttLib import TTFont #pip install fontTools def save_woff(response):woff response[woff]woff_file base64.b64decode(woff.enc…

K8S 哲学 - 服务发现 services

apiVersion: v1 kind: Service metadata:name: deploy-servicelabels:app: deploy-service spec: ports: - port: 80targetPort: 80name: deploy-service-podselector: app: deploy-podtype: NodePort 主机端口分配方式 两个 name port 和 targetPort type 类型

【实验】使用docker-compose编排lnmp(dockerfile) 完成Wordpress 部署

环境准备 docker&#xff1a;192.168.67.30 虚拟机&#xff1a;4核4G 关闭防火墙 systemctl stop firewalld systemctl disable firewalld setenforce 0 安装docker 直接点击【复制】粘贴到xshell中即可&#xff0c; 执行过程中若出现睡眠(sleep)通过 kill -9 pid号 &#x…
最新文章