当前位置: 首页 > news >正文

【MySQL】(11) 索引

一、什么是索引

        不同的数据结构组织数据的方式不同,导致查询数据的时间复杂度、空间复杂度不同。索引就是 MySQL 的一种用于提高查询效率的数据结构

        类似于字典中的目录,有笔画、偏旁、拼音三种文字查询方式,这就是三种索引。

        每更新、删除、增加数据,同时也会变更所有的索引。因此,虽然索引可以提高查询效率,但在数据增删改时过多的索引会导致效率低下。在应用程序中,查询的频率远远大于增删改的频率,使用索引是必要的。

二、使用哪种数据结构作为索引

1、Hash

        虽然 Hash 的查询时间复杂度为 O(1),但它仅支持等值查找(where id = 1),不支持范围查找和排序(两个相邻数据对应的 Hash 值是无序的)。

2、二叉搜索树

        N个节点,在树平衡时(AVL),最坏查询时间复杂度为 O(logN),极端情况单边树是 O(N)。支持范围查找(中序遍历)。

        但当 N 很大时,树非常高,磁盘 IO 次数过多(每通过父节点访问一次子节点,就会访问一次磁盘,磁盘 IO 是影响数据库效率的主要因素),导致搜索效率低下。且查询不同值的 IO 次数不稳定(处于树的层数不一样)。

3、N叉搜索树

        如B树(平衡N叉树),树高相对二叉树降低很多,但是范围查找仍然要遍历树,进行多次磁盘 IO,效率不够高。且非节点节点存储数据,导致存储键和指针的空间减小,及分支减少树高增加

4、B+树

        这是 MySQL 的索引的数据结构,支持范围查找,查询的时间复杂度为 O(logN),树高较小可控制 IO 次数。

        与 B 树的区别

  • 叶子节点可访问兄弟节点,MySQL 组织叶子节点使用的双向循环链表。(范围查找不用跨越层级而访问磁盘 IO
  • 相同树高下,查询任意元素的时间复杂度一样,性能稳定
  • 非叶子节点仅存数据的引用,叶子节点存储真实数据一个非叶子节点能有更多分支

三、页

1、什么是页

        在 MySQL 中,若用 InnoDB 存储引擎,.idb 文件就是存储一个表的数据和索引信息的文件。

数据以页为单位进行存储,也是内存与磁盘交互的最小单元,每次至少读取一页,16KB页内的地址是连续的,这样设计是因为:

  • 时间局部性:当前访问的数据,很有可能再次被访问。
  • 空间局部性:很有可能访问当前访问数据的邻近位置的数据。

        若下次查询的数据在当前页中,就直接从内存读取,减少访问磁盘,提高性能。

2、页的基本结构 

        MySQL 中有多种页,最常用的是索引页和数据页,结构如下:页头+数据主体+页尾

2.1、页头和页尾

        主要关注上一页页号下一页页号,构成双向链表,即 B+ 树中的非叶子节点存储的数据是页

2.2、页的主体

        最大行、最小行标识数据行从哪开始,到哪结束。next_record 记录下一个数据行的地址偏移量,整个主体构成单向链表:

        当有多条数据行,按照主键从小到大进行连接:

2.3、页目录

        一个页有 16 KB,可能会存储上百条数据行,在页内逐行遍历查找效率低下,因此引出页目录。将最小行单独为一组剩余的每 8 条为一组,页目录中的每个槽存储每组最后一条数据行的主键

        页内查询时,先根据数据行的主键值在页目录里进行二分查找找到对应槽,因为对应槽存储的每组最后一条数据的主键,我们想从组内第一条开始遍历,就找到对应槽的前一个槽,遍历查找数据:

2.4、数据页头

        保存当前页数据的相关信息。

四、B+树在 MySQL 索引中的应用

1、B+ 树构建示例

        创建主键为 1~ 16 的B+树,m = 3 个分支,一个节点最多存 m-1 = 2 条数据,分裂后左边存 m/2 = 1条数据 ,在叶子节点中插入:

        插入 1、2:

        插入 3:分裂,2 提上去,叶子节点中保留。

        插入 4:分裂,3 提上去。

        插入 5:分裂,4 提上去,再分裂,3 提上去,3 不保留。

2、在 B+ 树中查询一条记录

        查找主键为 4 的记录:

  • 找到 id = 4 所在数据页。

        访问根节点的索引页1,4 < 7 》访问左节点的索引页 2,3 < 4 < 5 》 访问中间叶子节点的数据页2。共进行 3 次磁盘 IO。

  • 在数据页中查找数据行。

3、计算 3 层高的 B+ 树最多可以存放多少记录

  • 索引页大小为 16 KB,假设有 n 个度,即 n 个引用(6字节),n-1 个主键(8字节),16 * 1024 = 6*n + 8*(n-1),n ≈ 1170 个分支。
  • 数据页大小为 16 KB,假设一条数据 1KB,那么可以存 16 条数据。
  • 3层树高:1170 ^ (3 -1) * 16 = 2190,2400。

        2 万多条数据,3 次磁盘 IO 即可实现查询,且性能稳定。 

五、索引分类

1、主键索引

        给表创建主键,会自动生成主键索引,InnoDB 将它作为聚集索引(数据的物理存储结构与列值(这里是主键)顺序一致)。由于一个表只能有一个物理存储顺序,因此一个表只有一个聚集索引。聚集索引可以直接通过索引值(主键)找到数据行

        建议一表一主键,没有唯一且非空列作为主键,就添加自增列

        一个表可以有多个非聚集索引,后面说的都是非聚集索引。

2、普通索引

       索引列可以是复合的,若是复合的,会按照索引列创建顺序排列,先按列1的排序,再按列2的排序。索引树中,索引页存储的是索引列值、子节点引用数据页存储的是索引列值、主键值

3、唯一索引

        索引列是唯一的,其它跟普通索引一样。

4、全文搜索

        索引列是文本类型(char、varchar、text),仅 MyISAM、InnoDB 支持。

5、聚集索引和非聚集索引

        聚集索引:

  • 一表必有一个,是表的物理存储结构。
  • 没有主键,默认用第一个唯一、非空列。
  • 没有合适的唯一列,自动生成 ROW_ID 自增。

        非聚集索引:

  • 又称二级索引,数据页必存储主键列或者指定列。
  • 回表查询:拿着在非聚集索引树中查找到的主键,回到聚集索引树中查询完整的数据行内容。

        列如下图查询语句:会先用 class_id 和 name 复合索引,在索引树中查找到 class_id、name、主键,再用主键到主键索引树查找 *。

6、索引覆盖

        需要查询的列刚好是非聚集索引的数据页存储的列,可以直接返回数据,不用回表查询。

六、索引的使用

1、对比是否使用索引对查询效率的影响

        当给表创建主键、外键、唯一键时,会自动生成对应的索引

        我们是 mysql 自带的 world 数据库中的 city 表,创建表时有主键、外键约束:

        自动创建了对应的索引:

        现查询一条信息:

        不使用索引:

        使用索引:

        这个表数据量小,效果不明显。当数据量庞大时,这个差距非常大。

2、索引的创建

查询 SQL 语法文档:MySQL :: MySQL 8.0 Reference Manual :: 15 SQL Statements

2.1、主键索引

        创建主键,自动生成主键索引,三种方式:

        alter 语句查询文档:

CONSTRAINT 表示指定一个唯一的约束名 symbol,index_name 是索引名。总结:

2.2、唯一索引

        不给索引指定名字,会默认生成一个唯一的索引名。


  2.3、外键索引

       总结

alter table 表名
add [constraint [约束名]] primary key/unique [index [索引名]] [索引数据结构] (索引列1[,...]);alter table 表名
add [constraint [约束名]] foreign key [index [索引名]] [索引数据结构] (索引列1[,...])
reference 主表名(引用列1[,...]);

2.4、普通索引

        创建时机:

  • 某列是频繁查询的列,或者出现慢查询,在该列上创建普通索引。
  • 数据库迭代过程中达到数据量(约超过十几万)创建(维护索引树需要时间成本,数据量小时,全表扫描可能比索引更快)。

2.5、注意事项

  • 高频查询慢查询列上创建索引。
  • 过多或不适合的索引会影响性能:占用存储空间;添加、修改、删除数据也会更新对应的索引。

3、索引的删除

3.1、主键索引

        因为一表只有一个主键,所以不用指定索引名

        如果主键列是自增的,需要先把自增去掉

        删除失败:

        去掉自增:

        删除主键索引:

3.2、其它索引

4、判断查询是否使用了索引

4.1、根据执行时间判断

        现有一张数据量达十几万的表:

        根据索引(主键id)和不根据索引(学号sn)查询(仅是单表查询,耗时就很久):

        现在创建一个 sn 索引,可以看到数据量庞大创建索引也很慢:

        索引建议的命名规则(索引类型+索引列名):

        查询该索引:

        使用创建好的普通索引查询,效率明显提高:

        如果是复合索引,索引列会有一个序列号,表示排序的优先级:

        查询时,查询条件包含 [列1] / [列1,列2]... 时索引生效。复合索引遵守最左原则,不可跳过左边的列使用右边的列。

        如直接使用列2查询,索引失效:

4.2、根据执行计划判断

        从时间上判断不一定准确,更好的办法是使用执行计划

        语法:在 SQL 语句前加 explain

        使用普通索引:

        观察真实使用索引情况 key,判断是否使用索引。

        type 类型:

        不使用索引(Using where 表示可通过 where 获得查询列):

        包含子查询,且外层语句使用主键查询(type 是 const 级别,效率高):

        覆盖查询(Using index 表示通过索引树就能获得查询列):

        回表查询(NULL,不能直接通过索引树获得查询列):

http://www.xdnf.cn/news/445447.html

相关文章:

  • 【Java学习笔记】equals方法
  • ElasticSearch重启之后shard未分配问题的解决
  • Rocky Linux 9.5 基于kubeadm部署k8s
  • 3天云南旅游规划
  • Kafka 消费者组进度监控方法解析
  • 【SSL部署与优化​】​​TLS 1.3的核心改进与性能优化​​
  • Java构造器封装继承重写多态
  • Doris
  • Kotlin 中 infix 关键字的原理和使用场景
  • A2DP、HFP、AVRCP分别是什么
  • STL,智能指针和线程安全自选锁读者写者问题
  • 蓝桥杯13届国B 完全日期
  • 【vue】生命周期钩子使用
  • 【行为型之访问者模式】游戏开发实战——Unity灵活数据操作与跨系统交互的架构秘诀
  • 关于Python 实现接口安全防护:限流、熔断降级与认证授权的深度实践
  • 2024年业绩增速大幅回退,泸州老窖未能“重回前三”
  • 使用Rust开发的智能助手系统,支持多模型、知识库和MCP
  • Go 语言 sqlx 库使用:对 MySQL 增删改查
  • Spring Boot requestBody postman
  • 人机环境体系的自主决策与机器系统的自主决策不同
  • 第二章:CSS秘典 · 色彩与布局的力量
  • 时源芯微| KY键盘接口静电浪涌防护方案
  • 【免杀】C2免杀技术(三)shellcode加密
  • ​Android学习总结之handler中源码解析和场景回答
  • scikit-learn在无监督学习算法的应用
  • 【愚公系列】《Manus极简入门》038-数字孪生设计师:“虚实映射师”
  • kaggle薅羊毛
  • 计算机操作系统(七)详细讲解进程的组成与特性,状态与转换
  • ESP32WIFI工具加透传
  • 生命之舞:创建,终止与等待,Linux进程控制的交响乐章