MySQL:索引特性

article/2025/8/17 8:16:36

索引

0. 预备知识

索引是一个“物美价廉”的特性,用来提高数据库的性能。不需要改程序、调SQL、只需要正确的创建索引,查询速度就能提高成百上千倍,但查询速度的提升也带来了插入、更新、删除速度的下降。

0.1 认识磁盘

MySQL对数据进行增删查改操作时,不可能直接访问磁盘硬件的,实际上对数据库的所有操作,都是在内存中进行的,由操作系统定期将数据刷新到磁盘上。

MySQL 给用户提供存储服务,而存储的数据在磁盘这个外设当中。磁盘是计算机中的一个机械设备,相比于计算机其他电子元件磁盘效率是比较低的。在加上IO本身的特征,可以认为,如何提交效率是 MySQL 的一个重要话题。

数据库文件,本质其实就是保存在磁盘的盘片,也就是上面的一个个扇区当中。当然,数据库文件很大也很多,一定需要占据多个扇区的。

所以最基本的找到一个文件的全部,本质就是在磁盘找到所有保存文件的扇区。而我们能够定位任何一个扇区,那么便能找到所有扇区,因为查找方式是一样的。

那么如何定位扇区呢?

磁盘的每盘都是双面的,大小完全相等。那么同半径的磁道整体上便构成了一个柱面,每个盘面都有一个磁头,所以我们只需要知道磁头、磁道、扇区对应的编号,即可在磁盘上定位所要访问的扇区。

这种磁盘数据定位方式叫做 CHS。不过实际系统软件使用的并不是 CHS 而是 LBA,一种线性地址,可以想象成虚拟地址与物理地址。系统将 LBA 地址最后会转化成为 CHS,交给磁盘去进行数据读取。

操作系统和硬盘如何交互?

如果操作系统直接使用硬件提供的数据大小进行交互,那么系统的IO代码就和硬件强相关。换言之,如果硬件发生变化系统必须跟着变化,从目前来看单次lO512字节太小了,会带来效率的降低。系统读取磁盘是以块为单位的,基本单位是 4KB 。

磁盘随机访问与连续访问?

随机访问:本次和上次lO给出扇区地址不连续,这样磁头在两次IO操作之间需要比较大的寻道动作才能重新进行IO。
连续访问:本次和上次 lO 结束的扇区地址是连续的,那磁头就能很快的开始这次IO操作,这称为连续访问。

随机访问和连续访问的效率差别非常大,可以说是10倍到20倍的差距。

0.2 MySQL数据交互

而MySQL作为一款数据库软件,它有着更高的IO场景,需要提高基本的IO效率。MySQL使用16KB进行IO交互,即MySQL和磁盘进行数据交互的基本单位是16KB。这个基本数据单元在 MySQL 里叫做 page

其实是MySQL和内核使用16KB交互,内核和磁盘使用4KB交互,但我们不关心系统,假定MySQL直接和磁盘交互。

MySQL中的数据文件,是以page为单位保存在内存当中的。MySQL的数据交互,都需要通过计算找到对应的位置。后续涉及到磁盘和内存的IO的基本单位就是Page。

为了更高的效率,一定要尽可能的减少系统和磁盘IO的次数。

 

1. 索引原理

为何MySQL和磁盘进行IO交互的时候,要采用Page的方案进行交互呢?

按Page大小进行交互,整个Page会被加载到MySQL的内存池中,这里完成了一次IO。往后如果在查找,完全不需要进行磁盘IO,而是直接在内存中进行了。所以就在单Page里面,大大减少了IO的次数。

有很大概率用户一定下次找的数据就在这个Page,因为有局部性原理。往往IO效率低下的最主要矛盾不是IO单次数据量的大小,而是IO的次数。

从现在开始,我们探讨的数据全部以Page的形式保存在MySQL内部缓冲区中了,和磁盘没有任何关系。

1.1 理解Page

MySQL中任意时刻都可能存在大量的Page页存在于MySQL内部,MySQL本身一定要对Page进行管理。

//伪代码
struct Page {struct Page* next;struct Page* prev;char buffer[16kb];
};

MySQL中的数据往往都是封装成Page结构体的形式,保存在内存中,单个大小16KB。

  • 多个Page结构体之间使用前后指针构成双向链表。
  • 单个Page结构体内部多条记录也是使用链表形式存储。

**因为有主键的原因,MySQL会自动将我们插入的数据,按主键的递增顺序进行排序。**那么为什么要进行排序呢?

插入数据时排序的目的,就是优化查询的效率。首先数据得有序才能方便查找。

1.2 主键索引

单页情况

如何提高在单个Page中的查找效率呢?

线性有序的数据可以类比一本书,书的页码从前到后一次排布。而书有目录,目录表明了所在章节的页码范围,在查找指定知识点的时候,可以先从目录找到所在章节,到指定章节再线性查找到指定位置。

针对单页Page我们也可以添加“目录”,如果我们想查找指定位置数据,先找目录到大体位置在线性查找即可。这是一种以空间换时间的做法。

从目录开始查找就能一次性排除更多数据,当然这一切也是建立在数据有序的基础上。

多页情况

Page之间也是以链表的形式组织的,只能线性查找的话效率太低。如何提高在多个Page中的查找效率呢?

因为我们需要保证数据有序,再次新插入的数据不一定会被放在最后一个Page的末尾,也有可能放在中间某个Page的中间位置。

这样的单纯用链表链起来的结构也有效率问题:

  1. 如果我们需要靠后的Page,但为了线性遍历,必须要把之前的所有Page导入内存,这样会造成大量IO。
  2. 就算需要的Page已经在内存中,线性遍历多个Page的方式也是问题。

解决思路就是给多个Page也搭配上“目录”:把多个Page看作多个记录,把Page中的最小记录的键值作为Page的键值,为其标上目录

  • 创建一个Page其中记录用来存储目录项,目录项由键值和指针构成,指向其下Page最小键值的记录。
  • 页内目录管理的是记录,这种目录管理的是页。

**目录页本质也是页,普通页存储的是用户数据,目录页存储的是普通页的地址。**这样一次查找就能排除多页,效率更高。

不过我们还需要一个管理页(可能是多个)存储其下所有目录页的最小键值,用来来管理其下所有目录页。这样每次查找从顶层管理页开始,逐步向下查找,这也就是B+树模型。

所谓索引,就是在MySQL内部,将热点数据存储在Page页,再以B+树的形式将所有Page页进行组织,形成数据结构与其配套的查找算法。

  • Page分为目录页和数据页。目录页只放各个下级Page的最小键值。
  • 自顶向下找,只需要加载部分目录页到内存,即可完成算法的整个查找过程,大大减少了IO次数。

如此就解决了我们最初链表方案的两个缺陷:线性遍历和大量IO。缺点是略降增删的效率,因为插入或删除会破坏底层数据页的结构,会向上影响到目录页。

为什么选择B+树,而不是其他数据结构?

数据结构劣势
链表只能线性遍历,效率太低
二叉搜索树退化问题,可能退化成为线性结构
AVL和红黑树二叉结构,导致树整体过高过瘦。过高需要加载Page就多IO效率低,过瘦排除的数据少
Hash散列随机的算法特征决定了范围查找时效率不行,只能一个一个找
B树底层无法线性遍历,范围查找效率不高。树体存储数据导致树过高

1.3 聚簇索引和非聚簇索引

概念存储引擎解释
聚簇索引InnoDB把索引和数放在一块
非聚簇索引MyISAM把索引和数据分离,叶节点不存储数据,只存指针

MySQL除了默认会建立主键索引外,用户也有可能以其他字段建立索引,一般这种索引叫做辅助索引或者普通索引。

  • 对于MyISAM,建立普通索引和主键索引没有差别,无非就是主键不能重复,而非主键可重复。
  • 对于InnoDB,建立普通索引,叶结点也不保存数据,而保存所在记录的主键,需要回表查询。

InnoDB普通索引不存数据而存主键,原因很简单浪费空间。而MyISAM因为其不管主键索引还是普通索引只存地址。

 

2. 索引操作

2.1 创建主键索引

创建表的时候设置主键或者后期追加设置主键,都会自动创建主键索引模型。

# 方式一
create table `tb_name` (`field` datatype primary key [, ...]);
# 方式二
create table `tb_name` (`field` datatype, primary key (id));
# 方式三
alter table `tb_name` add primary key (id);

2.2 创建唯一索引

# 方式一
create table `tb_name` (`field` datatype unique key [, ...]);
# 方式二
create table `tb_name` (`field` datatype, unique(`field`));
# 方式三
alter table `tb_name` add unique key (id);

如果建表时指定唯一键,则MySQL自动为其建立唯一键索引。

2.3 创建普通索引

# 创建表时指定索引
create table `tb_name` (`field` datatype [, ...], index(`field`));
# 追加索引
alter table `tb_name` add index(`field`);
# 追加索引
create index `idx_name` on `tb_name`(`field`);

2.4 查询索引

# 1
show index from `tb_name`;
# 2
show index from `tb_name` \G

2.5 删除索引

# 删除主键索引
alter table `tb_name` drop primary key;
# 删除唯一索引
alter table user3 drop index `index_name`;
# 删除普通索引
alter table user3 drop index `index_name`;

2.6 创建全文索引

我们目前所有的索引建立都是基于一列一个字段的,而且这字段的内容都不大。假如要对大量文字作索引,会使用到全文索引。目前只有MyISAM支持全文索引,不支持中文。

待补充。

2.7 索引创建原则

  • 比较频繁作为查询条件的字段应该创建索引;
  • 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件;
  • 更新非常频繁的字段不适合作创建索引;
  • 不会出现在where子句中的字段不该创建索引。

http://chatgpt.dhexx.cn/article/Dus1BUSc.shtml

相关文章

MySQL的索引与事务

作者:敲代码の流川枫 博客主页:流川枫的博客 专栏:和我一起学java 语录:Stay hungry stay foolish 给大家推荐一款好用的神器Apifox Postman Swagger Mock JMeter。集接口文档工具、接口Mock工具、接口自动化测试工具、接…

什么是 MySQL 索引?

什么是索引? 假设我们有一张数据表 employee(员工表),该表有三个字段(列),分别是name、age 和address。假设表employee有上万行数据(这公司还真大),现在需要从这个表中查找出所有名字是‘ZhangSan’的雇员信…

MySQL 索引结构

前言 在上一篇 MySQL 索引类型 中,我们已经了解了索引的基本概念以及分类,那么,索引的结构是什么样的?为什么索引可以这么快?这一篇文章将继续探讨索引的实现原理和数据结构,主要介绍 B 树索引和 Hash 索引…

MySQL中索引的使用方法

1. 为什么要加索引? ​一般的应用系统,读写比例在10:1左右,而且插入和一般的更新操作很少出现性能问题,遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,所以查询语句的优化显然是重中之…

MySQL 索引概览

前言 在 SQL 优化中,索引是至关重要的一环,能给查询效率带来质的飞跃,但是索引并不是万能的,不合理的索引设计甚至会拖慢查询效率。本文将详细介绍索引的概览和分类,并讨论使用索引时应该权衡的要素,关于索…

MYSQL的索引和存储引擎

文章目录 MYSQL的索引和存储引擎介绍索引的分类单列索引-普通索引单列索引-唯一索引单列索引-主键索引组合索引全文索引空间索引 索引内部原理剖析索引内部原理-Hash算法索引内部原理-二叉树和二叉平衡树索引内部原理-BTREE树MyISAM存储引擎InnoDB存储引擎 索引的特点索引的创建…

mysql 索引使用与优化

前言 索引对有一定开发经验的同学来说并不陌生,合理使用索引,能大大提升sql查询的性能,可以这么讲,随着业务数据量的不断增长,优化系统的响应速度,很大程度上可以说就是集中在索引的优化上; mysql索引原理 在正式了解与学习mysql索引之前,先对mysql的索引原理再次回…

MySql之索引

1.索引概述 MySql官方对索引的定义为:索引是帮助MySql高效获取数据的数据结构。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用数据,这样就可以在这些数据结构上实现高级查找算法&#xf…

Mysql 索引

图片来源网络,侵删。图片来源于掘金小册 索引 Mysql 的索引类型有很多种,Hash索引,B树索引,B树索引和全文索引。Mysql有多种存储引擎,每个存储引擎对索引的支持可能会不同。 What Mysql 索引是能改善数据库表随机访…

一文搞懂 MySQL 索引

一文搞懂 MySQL 索引 1、MySQL 索引 简介 1.1、MySQL 索引 是什么? 索引是一个单独的、存储在 磁盘 上的 数据库结构 ,包含着对数据表里 所有记录的 引用指针。 1.2、 MySQL 索引 的存储类型有哪些? MySQL中索引的存储类型有两种&#xff0c…

一文搞懂MySQL索引所有知识点(建议收藏)

Mysql索引 索引介绍 索引是什么 官方介绍索引是帮助MySQL高效获取数据的数据结构。更通俗的说,数据库索引好比是一本书前面的目录,能加快数据库的查询速度。 一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往是存储…

python中的%用法

python中%: 1. 求模运算,相当于mod,也就是计算除法的余数,比如5%2就得到1。 2. %还用在python的格式化输出,比如: 说明如下: %[(name)][flags][width].[precision]typecode (name) 为命名 fl…

python中的消息弹窗

在写python代码中,经常要弹窗提示一下消息情况,因为有时候我同时用了多个ui框架,比如tkinter,pyqt等,经常找不到合适的弹窗模块。因此梳理了一下几种弹窗方案。 一、采用windows自带的api(需要导入win32api) 特别强调采用这种方案,这种方案的优势就是弹窗模态,并不需…

Python 中的\r 字符

今天遇到了\r,然后就比较懵了,这里简单记录一下\r字符在Python中的应用。 \r:将光标回退到开始位置 先来看一个示例代码: import timetext "Hello\rWorld!" for i in text:time.sleep(0.5)print(i, end"")…

python中flag的用法_python中flag什么意思

python中flag一般就是标记、标识的意思 比如&#xff1a;&#xff08;推荐学习&#xff1a;Python视频教程&#xff09;#!/usr/bin/python # -*- coding: UTF-8 -*- x 7 i 1 flag 0 while i < 100: if (x%2 1) and (x%3 2) and (x%5 4) and (x%65): flag 1 e…

python中result的用法_python中result的用法

Python中%(number,result)是什么意思 浮点型(Float) Python的浮点数就是数学中的小数&#xff0c;类似C语言中的double。 在运算中&#xff0c;整数与浮点数运算的结果是浮点数. 浮点数也就是小数&#xff0c;之所以称为浮点数&#xff0c;是因为按照科学记数法表示时&#xf…

解决python中文乱码问题

python输出中文乱码的问题相信大家都遇到过 那么应该如何解决呢&#xff1f; 一、修改系统变量 依次打开 设置->系统->关于->高级系统设置->环境变量->新建系统变量&#xff0c;新变量的变量名是&#xff1a;PYTHONIOENCODING&#xff0c;变量值是&#xff1…

python中value的含义_python中value的意思

python语句s = str(value)是什么意思呢 把value转成字符串,然后赋值给变量s 比如说,s=str(100)以后。 Python 比如有一串数据的话里面 value[:-1]是什么value[:-1] : value 应该是一个列表/元组, value[:-1]表示其最后一个元素 python 如何将字典中的value值CSS布局HTML小编…

python中注释

python中注释 在python中的注释一般分为单行注释、多行注释以及文档注释。 注释描述 在实际开发过程中&#xff0c;有效的代码注释不仅可以提升个人的工作效率&#xff0c;快速了解自己的程序情况&#xff0c;在团队协作开发过程中可以更加方便地让同事学习和调用你的代码。单…

python中的各种符号

运算符描述实例算术运算符%取模 - 返回除法的余数b % a 输出结果 0**幂 - 返回x的y次幂a**b 为10的20次方&#xff0c; 输出结果 100000000000000000000//取整除 - 返回商的整数部分&#xff08;向下取整&#xff09; >>> 9//2 4 >>> -9//2 -5 赋值运算…