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

10.MySQL索引特性

MySQL索引特性

索引的概念

认识磁盘

磁盘的结构

磁盘的随机访问(Random Access)与连续访问(Sequential Access)

MySQL与磁盘交互的基本单位

Buffer Pool

索引的理解

观察主键索引现象

推导主键索引结构的构建

索引结构可以采用哪些数据结构

聚簇索引 VS 非聚簇索引

索引操作

创建主键索引

创建唯一索引

创建普通索引

创建全文索引

查询索引

删除索引

索引创建原则


MySQL索引特性

索引的概念

数据库表里存储的数据都是以记录为单位的,如果直接一条条遍历表中的数据记录进行查询,那时间复杂度就是O(N),这在海量数据场景下简直是灾难。这时候索引的价值就体现出来了——它能让你的查询速度提升成百上千倍,前提是得用对了索引。

索引的本质是数据库底层为表中的数据记录构建的特定数据结构。有了这个结构,查询时就能像查字典一样快速定位目标数据。不过要注意,索引虽然提升了查询速度,但在增删改操作时会拖慢效率。因为每次修改数据,不仅要执行对应操作,还得维护底层的数据结构。

常见的索引类型有四种:

  1. 主键索引(primary key):唯一且非空,一张表只能有一个
  2. 唯一索引(unique):确保字段值的唯一性,允许NULL值
  3. 普通索引(index):最基础的索引类型,没有任何限制
  4. 全文索引(fulltext):专门用于文本内容的模糊匹配

举个实际案例,假设我们创建了一个包含800万条记录的员工表EMP。在没有索引的情况下,查询某个员工信息平均需要4.5秒。但当我们给工号字段建立索引后,同样的查询几乎检测不到耗时。这就是索引带来的质变——从线性扫描到二分查找的飞跃。


认识磁盘

磁盘的结构

要理解MySQL索引的底层原理,必须先了解磁盘的物理结构。磁盘主要由以下几个关键部件组成:

  • 永磁铁:保证磁盘磁性的稳定性
  • 音圈马达:读取数据的核心部件
  • 主轴:驱动磁盘高速旋转
  • 磁头:负责读写数据
  • 盘片:存储数据的实际载体

每个盘片的表面被划分为多个同心圆磁道,每个磁道又被分割为若干个扇区。传统扇区大小是512字节,但现代硬盘已普遍采用4KB(4096字节)扇区。

定位扇区采用CHS寻址方式:

  1. Cylinder(柱面):确定磁道位置
  2. Head(磁头):确定盘面位置
  3. Sector(扇区):确定具体扇区

不过操作系统实际使用的是LBA(Logical Block Address)逻辑区块地址来管理磁盘,这实现了硬件层和软件层的解耦。

磁盘的随机访问(Random Access)与连续访问(Sequential Access)

磁盘访问效率的关键在于访问模式:

  • 随机访问:访问的扇区地址不连续,需要频繁移动磁头,效率低下
  • 连续访问:访问的扇区地址连续,磁头移动少,效率高

举个生活化的例子:就像图书馆找书,如果每次都要跑不同的书架找书(随机访问),肯定比连续在一排书架上找书(连续访问)慢得多。


MySQL与磁盘交互的基本单位

MySQL与磁盘交互的基本单位是16KB的Page(页),这是InnoDB存储引擎的核心数据单元。通过show variables like 'innodb_page_size';可以看到这个设置。

为什么不是操作系统常用的4KB?这涉及到局部性原理的应用。当读取某条记录时,MySQL会一次性加载整个Page(16KB)到内存。统计显示,下一次访问的数据有较大概率就在同一Page中,这样就减少了磁盘IO次数。

Buffer Pool

MySQL启动时会预先申请一块内存区域叫做Buffer Pool,用来缓存从磁盘加载的数据Page。CRUD操作都发生在Buffer Pool中,之后再以特定策略刷新回磁盘。

这里有个有趣的细节:MySQL和磁盘的交互虽然是以16KB为单位,但实际经过了两层缓冲:

  1. Buffer Pool → 内核缓冲区:16KB单位
  2. 内核缓冲区 → 磁盘:4KB单位

这就像快递中转站,先用大卡车(16KB)运输到中转站,再用小货车(4KB)分发到各个网点。


索引的理解

观察主键索引现象

创建一个简单的用户表:

CREATE TABLE user (id INT PRIMARY KEY,age INT,name VARCHAR(20)
);

即使插入数据时故意打乱顺序:

INSERT INTO user VALUES (3,25,'Tom'), (1,30,'Jerry'), (2,28,'Lucy');

查询结果却总是按id有序排列。这是因为MySQL会自动对主键数据进行排序存储,这是构建索引结构的基础。

推导主键索引结构的构建

单个Page

每个Page内部的数据记录以单链表形式组织,但为了加速查询,引入了页内目录。假设某个Page存储了以下数据:

idname
1Alice
3Bob
5Carol
7Dave

页内目录可能包含这些目录项:

  • 1 → 指向id=1的记录
  • 5 → 指向id=5的记录

查询id=6时,先找到页内目录中5对应的记录,再往后遍历即可。

多个Page

随着数据量增长,单Page无法容纳所有数据。这时需要多个Page协同工作。每个Page的Page结构体包含:

  • 数据区:存储实际记录
  • 页内目录:加速单Page内查询
  • 系统信息:如Page编号、类型等

页目录的进化

为了加速跨Page查询,引入了页目录:

  1. 页内目录:管理单个Page内的数据
  2. 页目录:管理多个Page
  3. 多级页目录:构建B+树结构

最终形成的就是B+树索引结构:

  • 叶子节点存储完整数据记录
  • 非叶子节点存储目录项
  • 叶子节点之间通过指针连接

索引结构可以采用哪些数据结构

哈希表

优点:O(1)的查询速度
缺点:不支持范围查询,内存消耗大

B树

  • 非叶子节点存储数据
  • 会导致树的高度增加
  • 范围查询效率一般

B+树(MySQL选择)

  • 非叶子节点仅存储索引
  • 叶子节点存储数据且相互连接
  • 支持高效范围查询
  • 适合磁盘存储特性

实际存储引擎支持情况

存储引擎支持索引类型
InnoDBB+树
MyISAMB+树
MEMORY哈希、B+树
NDB哈希、B+树

聚簇索引 VS 非聚簇索引

InnoDB的聚簇索引

InnoDB采用聚簇索引,数据直接存储在主键索引的叶子节点中。每个表必须有一个聚簇索引,可以是显式定义的主键,也可以是InnoDB自动生成的隐藏主键。

优势:

  • 数据访问更快,聚集索引将数据和索引存储在一起
  • 范围查询效率高

缺点:

  • 插入顺序影响性能,最好使用自增主键
  • 更新主键代价较高

MyISAM的非聚簇索引

MyISAM使用非聚簇索引,数据和索引分开存储。无论是主键索引还是普通索引,叶子节点都存储数据的物理地址。

优势:

  • 插入速度更快
  • 支持压缩表

缺点:

  • 查询需要两次IO:先查索引,再查数据
  • 不支持事务

文件结构差异:

  • InnoDB:xxx.frm(表结构) + xxx.ibd(数据和索引)
  • MyISAM:xxx.frm(表结构) + xxx.MYD(数据) + xxx.MYI(索引)

索引操作

创建主键索引

方式一:建表时指定

CREATE TABLE user (id INT PRIMARY KEY,name VARCHAR(20)
);

方式二:建表时最后声明

CREATE TABLE user (id INT,name VARCHAR(20),PRIMARY KEY(id)
);

方式三:建表后添加

ALTER TABLE user ADD PRIMARY KEY(id);

注意:添加主键前要确保字段满足非空且唯一。

创建唯一索引

方式一:建表时指定

CREATE TABLE user (id INT PRIMARY KEY,email VARCHAR(50) UNIQUE
);

方式二:建表时最后声明

CREATE TABLE user (id INT,email VARCHAR(50),PRIMARY KEY(id),UNIQUE(email)
);

方式三:建表后添加

ALTER TABLE user ADD UNIQUE(email);

唯一索引允许NULL值,但只能有一个NULL。

创建普通索引

方式一:建表时声明

CREATE TABLE user (id INT PRIMARY KEY,name VARCHAR(20),INDEX idx_name(name)
);

方式二:建表后添加

ALTER TABLE user ADD INDEX idx_name(name);

方式三:使用CREATE语句

CREATE INDEX idx_name ON user(name);

普通索引最灵活,允许重复和NULL值。

创建全文索引

适用于大文本字段的模糊匹配:

CREATE TABLE articles (id INT PRIMARY KEY,title VARCHAR(200),body TEXT,FULLTEXT(title, body)
);-- 查询方式
SELECT * FROM articles WHERE MATCH(title, body) AGAINST('database');

注意:

  • MyISAM原生支持全文索引
  • InnoDB从MySQL 5.6开始支持
  • 最小搜索长度受ft_min_word_len参数限制

查询索引

方式一:SHOW KEYS

SHOW KEYS FROM user;

输出字段详解:

  • Key_name:索引名称
  • Seq_in_index:列在索引中的顺序
  • Column_name:索引字段名
  • Cardinality:索引基数,越大越好
  • Index_type:索引类型(BTREE/FULLTEXT/HASH)

方式二:SHOW INDEX

SHOW INDEX FROM user;

功能与SHOW KEYS相同,输出格式更详细。

方式三:DESC

DESC user;

简单查看索引信息,适合快速浏览。

删除索引

删除主键索引

ALTER TABLE user DROP PRIMARY KEY;

注意:删除主键前要确保没有外键依赖。

删除非主键索引

ALTER TABLE user DROP INDEX idx_name;
-- 或者
DROP INDEX idx_name ON user;

删除索引会影响查询性能,操作前需评估。


索引创建原则

  1. 高频查询字段优先
    经常出现在WHERE、JOIN、ORDER BY中的字段适合创建索引。

  2. 唯一性要求高的字段
    唯一性差的字段(如性别)创建索引效果有限,但组合索引中可能有用。

  3. 避免频繁更新的字段
    更新索引会带来额外开销,如计数器字段不适合做索引。

  4. 组合索引遵循最左前缀原则
    对于idx_name_age(name, age)索引:

    • WHERE name='Tom' 有效
    • WHERE name='Tom' AND age=25 有效
    • WHERE age=25 无效
  5. 控制索引数量
    每个索引都会占用磁盘空间并影响写入性能,建议单表索引不超过5个。

  6. 使用前缀索引优化长字段

    CREATE INDEX idx_title ON articles(title(20));
    

    对VARCHAR(255)字段只索引前20个字符,节省空间。

  7. 区分度计算公式
    Cardinality / 表记录数 > 0.1 时索引效果较好

举个电商系统的例子:订单表的订单号字段适合创建主键索引,用户ID字段适合创建普通索引,而订单状态字段可能不适合单独创建索引,但可以作为组合索引的一部分。

通过合理使用索引,可以将原本需要扫描百万级数据的查询优化到只需几次磁盘IO,这就是数据库调优的精髓所在。

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

相关文章:

  • dify中解决docx上传文件报错问题
  • 泰迪杯特等奖案例深度解析:基于量子启发优化与多尺度时空建模的港口物流智能调度系统
  • 如何应对敏捷转型中的团队阻力
  • 【位运算】丢失的数字(easy)
  • Linux进程调度:从时间片到实时任务的交响乐
  • C++——智能指针 unique_ptr
  • 【leetcode】9. 回文数
  • Hadoop大数据集群深度实践:源码分析、参数调优与自动化运维平台选型全解
  • 知识宇宙-学习篇:程序员调试思维
  • PyTest框架学习
  • docker镜像下载到本地,并导入服务器
  • spring4第6课-bean之间的关系+bean的作用范围
  • [C]extern声明变量报错:undefined reference终极解决方案
  • 《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- 第一篇:MIPI CSI-2基础入门
  • 【AAOS】【源码分析】用户管理(二)-- 用户启动
  • APx500录制波形
  • Qt Creator工具编译器配置
  • Oj系统测试报告
  • 第3章(新)Day3-Python逻辑语句
  • Java 创建线程池的几种方式
  • Python基础:文件简单操作
  • OpenCV CUDA模块图像处理------创建CUDA加速的Canny边缘检测器对象createCannyEdgeDetector()
  • 使用 useSearchParams 的一个没有触发控制台报错的错误用法
  • STL 库基础概念与示例
  • 洛谷每日1题-------Day39__P1697 [USACO18JAN] Lifeguards B
  • Vue 生命周期全解析:从创建到销毁的完整旅程
  • Redisson - 实现延迟队列
  • 通过ca证书的方式设置允许远程访问Docker服务
  • 吴恩达机器学习讲义概述
  • 在虚拟宇宙中低语——进程间通信,Linux命名管道的前世今生