【MySQL】索引

目录

前言

[1. 索引](#1. 索引)

[2. 单个Page的理解](#2. 单个Page的理解)

[3. 索引的种类](#3. 索引的种类)

主键索引

普通索引

唯一索引

全文索引

复合索引

索引创建原则

[4. 索引失效的问题](#4. 索引失效的问题)

总结


前言

说起MySQL数据库,索引是绕不开的一个话题,索引这个概念对于初学者来说模糊又抽象,那索引到底是什么?本文就来聊一聊MySQL中的索引,如何理解索引,索引的使用等等一些问题;

1. 索引

索引顾名思义,在学习数组中,数组的下标就是一种索引,索引可以帮助我们快速查找到指定数据;数据库中的索引也是如此,合理的使用索引可以极大的提高数据库的查询速度;

数据库中的索引:提高数据库的性能,索引是物美价廉的东西了。不用加内存,不用改程序,不用调sql,只要执行正确的,查询速度就可能提高成百上干倍。但是天下没有免费的午餐,查询速度的提高是以插入、更新、删除create index的速度为代价的,这些写操作,增加了大量的IO。所以它的价值,在于提高一个海量数据的检索速度;

MySQL 数据库作为一款应用软件,可以想象成一个特殊的文件系统。一般来说,常见的文件系统多以 4KB 为单位进行 IO 操作,而 MySQL 数据库的 InnoDB 存储引擎默认是以 16KB 为单位进行 IO 的,这个基本单元我们称为 page。较大的页大小有助于减少磁盘 I/O 次数,提高数据读取效率,数据的存储和索引的组织都是以 page 为单位进行的。当然,文件系统的块大小并非绝对固定为 4KB,MySQL 的页大小也可以根据实际需求通过配置参数进行调整。

为了便于理解,首先需要建立以下的共识:

  • MySQL中的数据文件,是以page为单位保存在磁盘当中的
  • MySQL的 CURD(增删查改)操作大部分都需要计算(只要涉及计算,就需要CPU参与,有CPU参与,就需要先把数据加入到内存中);
  • 在特定时间内,数据一定是磁盘中有,内存中也有。后续操作完内存数据之后,以特定的刷新策略,刷新到磁盘。而这时,就涉及到磁盘和内存的数据交互,也就是IO了。而此时IO的基本单位就是Page
  • 为了更好的进行上面的操作,MySQL服务器在内存中运行的时候,在服务器内部,就申请了被称为 Buffer Pool的的大内存空间,来进行各种缓存。其实就是很大的内存空间,来和磁盘数据进行IO交互。
  • 想要更高的效率,就一定要尽可能的减少系统和磁盘IO的次数(往往IO效率低下的最主要矛盾不是IO单次数据量的大小,而是IO的次数);

2. 单个Page的理解

不同的 Page ,在 MySQL中,都是 16KB,使用 prev 和 next 构成双向链表(这里默认是 InnoDB 存储引擎,下文也都是);

MySQL 会默认按照主键给我们的数据进行排序,为了优化查询的效率 ;

在查询某条数据的时候直接将一整页的数据加载到内存中,以减少硬盘IO次数,从而提高性能;

那么如果有1千万条数据,就一定需要多个Page来保存1千万条数据,多个Page彼此使用双链表链接起来,而且每个Page内部的数据也是基于链表的。那么,查找特定一条记录,也一定是线性查找。这效率也太低了;有什么解决办法吗?------引入目录;在读一本书的时候都会有目录,查阅目录可以帮助我们快速的定位数据的页数;

单个页引入目录

比如,我们要查找id=4记录,之前必须线性遍历4次,才能拿到结果。现在直接通过目录2[3],直接进行定位新的起始位置,提高了效率;MySQL 会默认按照主键给我们的数据进行排序,因此可以很方便的引入目录;

多个页引入目录

在单表数据不断被插入的情况下,MySQL 会在容量不足的时候,自动开辟新的Page来保存新的数据,然后通过指针的方式,将所有的Page组织起来;

引入目录虽然可以提高一定的效率,但数据量一大,还是需要大量的线性遍历;怎么解决?------多级目录,给Page也加上目录;

目录页中的数据存放的就是指向的那一页中最小的数据。有数据,就可通过比较,找到该访问那个Page,进而通过指针,找到下一个page。

其实目录页的本质也是页,普通页中存的数据是用户数据,而目录页中存的数据是普通页的地址;存在一个目录页来管理页目录;

我们每次检索数据的时候,该从哪里开始呢?虽然顶层的目录页少了,但是还要遍历啊?------可以再加目录页;

这样的结构也就是B+树;至此,我们已经给我们的表user构建完了主键索引。随便找一个 id ,现在查找的Page数一定减少了,也就意味着IO次数减少了,那么效率也就提高了。

索引的本质,就是数据结构:B+树结构 ;

索引为什么使用B+树不使用其他数据结构?

效率问题;基本数据结构vector、list、效率不行;map查询时间复杂度O(logN);

hash查询速度O(1)为什么不用?

hash不擅长范围查询;一些数据库引擎也支持hash;综合B+树最优 ;

聚簇索引&非聚簇索引

非聚簇索引:索引Page和数据Page分离,也就是叶子节点没有数据,只有对应数据的地址;

其中InnoDB就支持聚簇索引: 是将索引和数据放在一起的;创建索引,本质也是创建B+树结构

3. 索引的种类

主键索引

上述示例中就是一个主键索引,根据主键构建的B+树为主键索引;它不允许有空值。一个表只能有一个主键索引,主键索引可以确保表中每行数据的唯一性,并且可以加快对主键列的查询速度。

创建表时创建索引:

sql 复制代码
CREATE TABLE table_name (
    column1 datatype PRIMARY KEY,
    column2 datatype
);

使用ALTER TABLE 语句创建

sql 复制代码
ALTER TABLE table_name ADD PRIMARY KEY (column1);

普通索引

普通索引是最基本的索引类型,它没有任何限制,仅用于提高查询速度。可以在单个列或多个列上创建。

在创建表时创建:

sql 复制代码
CREATE TABLE table_name (
    column1 datatype,
    column2 datatype,
    INDEX index_name (column1)
);

在已存在的表上创建:

sql 复制代码
CREATE INDEX index_name ON table_name (column1);

使用 ALTER TABLE 语句创建:

sql 复制代码
ALTER TABLE table_name ADD INDEX index_name (column1);

唯一索引

唯一索引要求索引列的值必须唯一,但允许有空值(NULL)。如果是多列唯一索引,则组合的值必须唯一。

在创建表时创建:

sql 复制代码
CREATE TABLE table_name (
    column1 datatype,
    column2 datatype,
    UNIQUE INDEX index_name (column1)
);

在已存在的表上创建:

sql 复制代码
CREATE UNIQUE INDEX index_name ON table_name (column1);

使用 ALTER TABLE 语句创建:

sql 复制代码
ALTER TABLE table_name ADD UNIQUE INDEX index_name (column1);

全文索引

全文索引主要用于在文本数据(如文章内容、评论等)中进行全文搜索。它可以对文本中的关键词进行索引,从而加快全文搜索的速度。MySQL 支持在 CHAR、VARCHAR 和 TEXT 类型的列上创建全文索引(但对中文的支持不是很好);

在创建表时创建:

sql 复制代码
CREATE TABLE table_name (
    column1 TEXT,
    FULLTEXT INDEX index_name (column1)
);

在已存在的表上创建:

sql 复制代码
CREATE FULLTEXT INDEX index_name ON table_name (column1);

使用 ALTER TABLE 语句创建:

sql 复制代码
ALTER TABLE table_name ADD FULLTEXT INDEX index_name (column1);

使用示例:

sql 复制代码
SELECT * FROM table_name WHERE MATCH(column1) AGAINST('keyword' IN NATURAL LANGUAGE MODE);

复合索引

复合索引也称为联合索引,是在多个列上创建的索引。使用复合索引时需要遵循最左匹配原则,即查询条件需要从索引的最左边列开始依次匹配。

最左匹配原则:最左匹配原则是指在使用复合索引进行查询时,MySQL(以 MySQL 数据库为例)会按照复合索引中列的顺序,从最左边的列开始依次匹配查询条件,直到遇到范围查询(如 >、<、BETWEEN、LIKE 以通配符开头)等条件停止匹配。也就是说,如果复合索引包含多个列,只有当查询条件中使用了索引最左边的列或者连续的左边几列时,索引才会被有效使用。

在创建表时创建:

sql 复制代码
CREATE TABLE table_name (
    column1 datatype,
    column2 datatype,
    INDEX index_name (column1, column2)
);

在已存在的表上创建:

sql 复制代码
CREATE INDEX index_name ON table_name (column1, column2);

使用 ALTER TABLE 语句创建:

sql 复制代码
ALTER TABLE table_name ADD INDEX index_name (column1, column2);

索引创建原则

  1. 频繁作为查询条件的字段应该创建索引
  2. 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件
  3. 更新频繁的字段不适合作为索引
  4. 不会在where子句中出现的,不适合创建索引;

4. 索引失效的问题

常见的索引失效情况:

  • 违背最左匹配原则:对于复合索引,只有查询条件按照索引列从左到右的顺序依次使用,索引才会被有效利用。若跳过某些列,可能导致索引失效
  • 对索引列使用函数或表达式:当在查询条件中对索引列使用函数或表达式时,数据库无法直接使用索引进行查找,可能会导致索引失效。
  • 类型不匹配:如果查询条件中的数据类型与索引列的数据类型不一致,数据库可能需要进行隐式类型转换,从而导致索引失效。
  • 范围查询后索引中断:在复合索引中,若存在范围查询(如 >、<、BETWEEN 等),范围查询之后的索引列将无法使用索引。
  • OR 连接条件 :当使用 OR 连接多个条件时,如果其中一个条件不使用索引,可能会导致整个查询的索引失效。

总结

以上便是本文的全部内容,希望对你有所帮助,感谢阅读!

相关推荐
weixin_425878231 小时前
Redis复制性能优化利器:深入解析replica-lazy-flush参数
数据库·redis·性能优化
左灯右行的爱情1 小时前
Redis数据结构总结-listPack
数据结构·数据库·redis
隔壁老王1562 小时前
mysql实时同步到es
数据库·mysql·elasticsearch
想要打 Acm 的小周同学呀2 小时前
Redis三剑客解决方案
数据库·redis·缓存
rkmhr_sef2 小时前
Redis 下载与安装 教程 windows版
数据库·windows·redis
库库林_沙琪马4 小时前
Redis 缓存穿透、击穿、雪崩:问题与解决方案
数据库·redis·缓存
Hanson Huang4 小时前
【存储中间件API】MySQL、Redis、MongoDB、ES常见api操作及性能比较
redis·mysql·mongodb·es
黄雪超4 小时前
大数据SQL调优专题——引擎优化
大数据·数据库·sql
LUCIAZZZ4 小时前
EasyExcel快速入门
java·数据库·后端·mysql·spring·spring cloud·easyexcel
落落落sss5 小时前
MongoDB
数据库·windows·redis·mongodb·微服务·wpf