当前位置: 首页 > 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,这就是数据库调优的精髓所在。

相关文章:

  • cv::FileStorage用法
  • Qt Creator工具编译器配置
  • Linux防火墙实战演练
  • 基于 PyTorch 的 VGG16 深度学习人脸识别检测系统的实现+ui界面
  • 机器学习的数学基础:线性模型
  • unix/linux,sudo,其高级使用
  • 在 Android 框架中,接口的可见性规则
  • Excel 透视表以及透视图应用(基础版)
  • 实验设计如何拯救我的 CEI VSR 28G 设计
  • Springboot——整合websocket并根据type区别处理
  • Git忽略规则.gitignore不生效解决
  • Dockerfile基础
  • 【Docker 从入门到实战全攻略(二):核心概念 + 命令详解 + 部署案例】
  • 【计算机网络】HTTPS
  • FTPS、HTTPS、SMTPS以及WebSockets over TLS的概念及其应用场景
  • JUC并发编程(二)Monitor/自旋/轻量级/锁膨胀/wait/notify/等待通知机制/锁消除
  • 结构型设计模式之Proxy(代理)
  • 基于VMD-LSTM融合方法的F10.7指数预报
  • 证券交易柜台系统解析与LinkCounter解决方案开发实践
  • 基于Axure+墨刀设计的电梯管理系统云台ERP的中保真原型图
  • 衢州网站建设专业的公司/郑州百度seo排名公司
  • 做网站哪家强/seo搜索优化是什么
  • 郑州专业做网站企业/买淘宝店铺多少钱一个
  • 上海第五届世界进口博览会/seo关键词推广
  • 网站建设公司前十名/如何进行网络推广营销
  • 湖南郴州市是几线城市/windows优化大师卸载