目录
[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);
索引创建原则
- 频繁作为查询条件的字段应该创建索引
- 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件
- 更新频繁的字段不适合作为索引
- 不会在where子句中出现的,不适合创建索引;
4. 索引失效的问题
常见的索引失效情况:
- 违背最左匹配原则:对于复合索引,只有查询条件按照索引列从左到右的顺序依次使用,索引才会被有效利用。若跳过某些列,可能导致索引失效
- 对索引列使用函数或表达式:当在查询条件中对索引列使用函数或表达式时,数据库无法直接使用索引进行查找,可能会导致索引失效。
- 类型不匹配:如果查询条件中的数据类型与索引列的数据类型不一致,数据库可能需要进行隐式类型转换,从而导致索引失效。
- 范围查询后索引中断:在复合索引中,若存在范围查询(如 >、<、BETWEEN 等),范围查询之后的索引列将无法使用索引。
- OR 连接条件 :当使用
OR
连接多个条件时,如果其中一个条件不使用索引,可能会导致整个查询的索引失效。
总结
以上便是本文的全部内容,希望对你有所帮助,感谢阅读!