MySQL 索引全景指南:从底层原理到实战优化
一、索引基础:本质与核心价值
索引是数据库的 “智能目录”,通过特定数据结构减少磁盘 I/O 次数,实现 “空间换时间” 的查询优化。其核心价值体现在查询效率的天壤之别:
- 无索引:全表扫描(时间复杂度 O (n))→ 100 万行数据需 100 万次磁盘 I/O,性能极差
- 有索引:结构化查找(B + 树时间复杂度 O (log n))→ 100 万行数据仅需 3 次 I/O,效率提升 30 万倍
💡 关键概念:I/O 次数 = 索引树高度InnoDB 默认索引页(节点)大小为 16KB,若主键为 INT(4 字节)+ 子节点指针(6 字节),单节点可存储约 1600 个关键字,树高与数据量的对应关系如下:
| 数据量 | B + 树高度 | 最大 I/O 次数 |
|---|---|---|
| 1600 条以内 | 1 | 1 |
| 1600~256 万条 | 2 | 2 |
| 256 万~40 亿条 | 3 | 3 |
| 40 亿~6400 亿条 | 4 | 4 |
二、索引核心结构:为什么 B + 树是最优解?
(一)四种树结构深度对比
| 树结构 | 结构特点 | I/O 计算(10 亿数据) | 适用场景 |
|---|---|---|---|
| 二叉搜索树 | 易退化链表(有序插入时) | 10 亿次 I/O | 完全不适合磁盘存储 |
| 红黑树 | 自平衡但单节点存 1 个 key | 30 次 I/O | 内存数据(如 Java TreeMap) |
| B 树 | 内部节点存 key+data | 4 次 I/O | 早期数据库(如 Oracle 旧版本) |
| B + 树 | 内部存 key + 叶子存 data + 双向链表 | 4 次 I/O | MySQL 默认(InnoDB/MyISAM) |
(二)B + 树的核心优势(磁盘存储黄金标准)
以 10 亿数据的四层 B + 树为例,结构示意图如下:
┌───────────────┐ ← 根节点(1层)
│ [200] │
└──────┬────────┘
│
┌──────────────┬───┴───┬──────────────┐ ← 内部节点(2层)
▼ ▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ [50, 100] │ │ [150, 180] │ │ [220, 250] │ │ [280, 300] │
└──────┬────┬───┘ └──────┬────┬───┘ └──────┬────┬───┘ └──────┬────┬───┘
│ │ │ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ← 叶子节点(3-4层)
┌───────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│10│20│30│40│...│ ⇄ │60│70│80│90│...│ ⇄ │110│120│130│140│...│ ⇄ │160│170│180│...│
└───────────────┘ └──────────────────┘ └──────────────────┘ └──────────────────┘
- 空间效率高:内部节点仅存 key,16KB 页可存 1600 个 key,树高显著降低
- 查询性能稳:所有查询最终落到叶子节点,路径长度一致(无极端情况)
- 范围查询快:叶子节点通过双向链表连接,
BETWEEN/ORDER BY无需回溯 - 数据存储密:叶子节点连续存储,磁盘预读(按页加载)效率高
三、存储引擎差异:InnoDB vs MyISAM 索引实现
(一)InnoDB:聚簇索引为核心的索引体系
InnoDB 以聚簇索引为基础,所有索引均基于 B + 树实现,内部节点仅存 key,无数据。
| 索引类型 | 叶子节点内容 | 是否回表 | 查询示例 | I/O 次数(1 亿数据) |
|---|---|---|---|---|
| 主键索引 | 完整数据行 | ❌ 否 | SELECT * WHERE id=20 | 3 次(根→内部→叶子) |
| 二级索引 | 主键值(如 id=20) | ✅ 是 | SELECT * WHERE user_id=13 | 6 次(3 次二级 + 3 次主键) |
🔑 关键逻辑:二级索引需 “回表”—— 先查二级索引树获主键,再查主键索引树获完整数据。
(二)MyISAM:索引与数据分离的非聚簇体系
MyISAM 支持主键索引,但与 InnoDB 的聚簇索引存在本质差异:
- MyISAM 的主键索引本质是 “带唯一性约束的普通 B + 树索引”,叶子节点存储数据文件的物理地址指针,而非完整数据行
- 若未显式指定主键,MyISAM 会默认创建一个隐藏的自增列作为主键,但其索引结构仍为非聚簇类型
| 索引类型 | 叶子节点内容 | 是否回表 | 查询示例 | I/O 次数(1 亿数据) |
|---|---|---|---|---|
| 主键索引 | 数据文件指针(如 0x100) | ❌ 否 | SELECT * WHERE id=20 | 4 次(3 次索引 + 1 次数据) |
| 二级索引 | 数据文件指针(如 0x200) | ❌ 否 | SELECT * WHERE user_id=13 | 4 次(3 次索引 + 1 次数据) |
🔑 核心差异:MyISAM 的主键索引无 “聚簇特性”,与二级索引结构一致(仅多唯一性约束);InnoDB 的主键索引是聚簇索引,直接关联完整数据,查询效率更高。
(三)实战对比:相同查询的执行流程
场景:SELECT * FROM orders WHERE user_id=13(表含 id=20、30 两条匹配数据)表结构:id INT PRIMARY KEY, user_id INT, amount DECIMAL(10,2), INDEX idx_user(user_id)
1. InnoDB 执行流程(需回表)
步骤1:查二级索引idx_user(3次I/O)
┌───────────────┐(根节点)→ ┌───────────────┐(内部节点)→ ┌───────────────┐(叶子节点)
│ user_id=13 │ │ user_id=13 │ │ 主键值: [20,30]│
步骤2:回表查主键索引(3次I/O/id)
┌───────────────┐(根节点)→ ┌───────────────┐(内部节点)→ ┌───────────────┐(叶子节点)
│ id=20 │ │ id=20 │ │ [20,13,200] │
│ id=30 │ │ id=30 │ │ [30,13,150] │
✅ 总I/O:3+3+3=9次
2. MyISAM 执行流程(无需回表)
步骤1:查索引idx_user(3次I/O)
┌───────────────┐(根节点)→ ┌───────────────┐(内部节点)→ ┌───────────────┐(叶子节点)
│ user_id=13 │ │ user_id=13 │ │ 指针: [0x100,0x200]│
步骤2:读数据文件(1次I/O,批量加载)
┌───────────────────────┐
│ 0x100→(20,13,200) │
│ 0x200→(30,13,150) │
└───────────────────────┘
✅ 总I/O:3+1=4次
四、索引类型全解析:从基础到特殊
(一)B + 树索引家族(核心类型)
1. 主键索引(InnoDB 聚簇特性 vs MyISAM 普通特性)
-
定义:以主键字段(
PRIMARY KEY)为索引键的 B + 树索引,所有存储引擎均支持,但实现差异显著:- InnoDB:主键索引是聚簇索引,叶子节点存储完整数据行,是表的 “核心索引”,一张表仅 1 个
- MyISAM:主键索引是普通 B + 树索引(仅多唯一性约束),叶子节点存储数据指针,与二级索引结构一致
-
核心特性:
- 唯一性约束:强制字段值不重复,且不允许
NULL(所有存储引擎通用) - 索引优先级:查询时优先使用主键索引(优化器默认选择高选择性索引)
- 唯一性约束:强制字段值不重复,且不允许
-
实战示例:
sql
-- 1. InnoDB表(主键索引为聚簇索引) CREATE TABLE innodb_users ( id INT NOT NULL AUTO_INCREMENT COMMENT '用户ID', username VARCHAR(50) NOT NULL COMMENT '用户名', PRIMARY KEY (id) -- 聚簇索引,叶子存完整数据 ) ENGINE=InnoDB; -- 2. MyISAM表(主键索引为普通索引) CREATE TABLE myisam_users ( id INT NOT NULL AUTO_INCREMENT COMMENT '用户ID', username VARCHAR(50) NOT NULL COMMENT '用户名', PRIMARY KEY (id) -- 普通B+树索引,叶子存数据指针 ) ENGINE=MyISAM; -- 主键查询对比 SELECT * FROM innodb_users WHERE id=10086; -- InnoDB:3次I/O(直接读叶子数据) SELECT * FROM myisam_users WHERE id=10086; -- MyISAM:4次I/O(索引+数据文件) -
关键结论:“主键索引” 是所有存储引擎的基础功能,但 “InnoDB 的主键索引 = 聚簇索引” 是其特有特性,MyISAM 无聚簇索引,需区分表述避免混淆。
2. 唯一索引(唯一性约束 + 查询优化)
-
定义:对具有唯一性约束的字段(
UNIQUE)建立的 B + 树索引,叶子节点存储主键值(InnoDB)或数据指针(MyISAM),核心作用是保证字段值不重复,同时优化查询。 -
核心特性(关于 NULL 的关键修正) :
- 允许字段值为
NULL,且支持多个NULL值(因 SQL 标准中NULL表示 “未知”,不与任何值相等,包括自身,故多个NULL不违反唯一性约束) - 一张表可创建多个唯一索引(如
username、email分别建唯一索引)
- 允许字段值为
-
实战示例(含多 NULL 验证) :
sql
-- 创建含唯一索引的表(允许email为NULL) CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, email VARCHAR(100) COMMENT '允许为NULL', -- 唯一索引:username(非NULL)、email(可NULL) UNIQUE INDEX idx_unique_username (username), UNIQUE INDEX idx_unique_email (email) ); -- 1. 插入重复username:报错(违反唯一性约束) INSERT INTO users (username, email) VALUES ('zhangsan', 'zhangsan@xxx.com'); INSERT INTO users (username, email) VALUES ('zhangsan', 'zhangsan2@xxx.com'); -- 错误:Duplicate entry 'zhangsan' for key 'idx_unique_username' -- 2. 插入多个NULL的email:成功(多个NULL不违反约束) INSERT INTO users (username, email) VALUES ('lisi', NULL); INSERT INTO users (username, email) VALUES ('wangwu', NULL); -- 成功:无报错 INSERT INTO users (username, email) VALUES ('zhaoliu', NULL); -- 成功:无报错 -- 3. 唯一索引查询(命中索引) SELECT id FROM users WHERE username='lisi'; -- 命中idx_unique_username SELECT id FROM users WHERE email IS NULL; -- 命中idx_unique_email,返回lisi、wangwu、zhaoliu的记录 -
适用场景:需保证字段唯一性的场景(如用户名、手机号),或需优化 “可 NULL 字段” 查询的场景(如未绑定邮箱用户的筛选)。
3. 普通单列索引(基础查询优化)
-
定义:对单个非主键、非唯一字段建立的 B + 树索引,无唯一性约束,叶子节点存储主键值(InnoDB)或数据指针(MyISAM)。
-
核心特性:仅用于优化查询,不限制字段值重复,支持多值匹配(如
WHERE age=25返回所有 25 岁用户)。 -
实战示例:
sql
-- 给users表添加普通索引(优化age字段查询) ALTER TABLE users ADD INDEX idx_normal_age (age); -- 普通索引查询(高频单条件场景) SELECT username, age FROM users WHERE age=25; -- 命中索引,且覆盖查询(无需回表) SELECT * FROM users WHERE age>30; -- 命中索引,范围查询(InnoDB需回表) -
适用场景:无唯一性要求的高频查询字段(如用户年龄、订单状态、商品分类)。
4. 联合索引(复合索引,多条件查询优化)
-
定义:对多个字段组合建立的 B + 树索引,按字段顺序构建索引键(如
(user_id, create_time)),叶子节点存储主键值(InnoDB)或数据指针(MyISAM)。 -
核心原则:最左前缀原则(查询需从左至右匹配,跳过左列则索引失效)。
-
实战示例:
sql
-- 创建订单表,添加联合索引(优化“用户+时间范围”查询) CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, create_time DATETIME NOT NULL, amount DECIMAL(10,2) NOT NULL, -- 联合索引:user_id(左1)→ create_time(左2)→ amount(左3) INDEX idx_combo_user_time_amount (user_id, create_time, amount) ); -- 有效查询(命中索引) SELECT * FROM orders WHERE user_id=13; -- 匹配左1列user_id SELECT amount FROM orders WHERE user_id=13 AND create_time>'2024-01-01'; -- 匹配左1+左2列,覆盖查询 SELECT * FROM orders WHERE user_id=13 AND create_time BETWEEN '2024-01-01' AND '2024-01-31' AND amount>200; -- 匹配左1+左2+左3列 -- 无效查询(索引失效) SELECT * FROM orders WHERE create_time>'2024-01-01'; -- 跳过左1列user_id SELECT * FROM orders WHERE user_id=13 AND amount>200; -- 跳过左2列create_time,仅左1列生效 -
适用场景:高频多条件查询(如 “用户的近期订单”“分类下的热门商品”),通过字段顺序优化过滤效率。
5. 前缀索引(字符字段空间优化)
-
定义:对字符类型字段(CHAR、VARCHAR、TEXT)的前 N 个字符建立的 B + 树索引,仅存储字段前缀作为索引键,减少索引空间占用。
-
核心注意:需保证前缀的 “选择性”(即前缀重复率低),避免因前缀重复导致索引过滤效果差。
-
实战示例:
sql
-- 场景:用户表email字段为VARCHAR(100),全字段索引占用空间大,建前缀索引 CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, email VARCHAR(100) NOT NULL COMMENT '用户邮箱(如zhangsan_123456@xxx.com)', -- 对email前15个字符建前缀索引(通过选择性计算确定N=15) INDEX idx_prefix_email (email(15)) ); -- 计算前缀选择性(判断N是否合适) -- 全字段选择性:SELECT COUNT(DISTINCT email)/COUNT(*) FROM users; -- 假设结果0.99(接近1,选择性高) -- 前缀选择性:SELECT COUNT(DISTINCT LEFT(email,15))/COUNT(*) FROM users; -- 结果0.98,接近全字段,N=15合适 -- 前缀索引查询(命中索引) SELECT id FROM users WHERE email='zhangsan_123456@xxx.com'; -- 前缀“zhangsan_123456@”匹配,命中索引 -
适用场景:长字符字段(如邮箱、手机号前缀、长文本标识),全字段索引空间占用过大时,用前缀索引平衡空间与查询效率。
(二)非 B + 树索引(特殊场景)
1. 哈希索引
-
结构原理:通过哈希函数将 key 映射为哈希值,建立 “哈希值→数据地址” 的映射表。
-
核心特点:
- 等值查询极快(时间复杂度 O (1)),无 I/O 随数据量增长的问题
- 不支持范围查询(>、<、BETWEEN)和排序
-
适用场景:高频等值查询(如用户登录验证
WHERE username='xxx') -
InnoDB 支持:自适应哈希索引(AHI),由 MySQL 自动创建维护,无需手动干预
2. 全文索引
-
结构原理:采用倒排索引(非 B + 树),通过 “分词→词项映射表” 实现文本检索。
-
倒排索引结构:
plaintext
词项(Term) → 文档列表(Document ID + 位置) "MySQL" → [(1, 5), (3, 2), (10, 8)] -- 存在于ID=1的第5个词、ID=3的第2个词... "索引" → [(1, 8), (2, 3), (5, 10)] -
关键流程:分词(英文按空格,中文需 ngram 插件)→ 过滤停止词(如 “the”“的”)→ 建立词项映射
-
使用语法:
sql
-- 创建全文索引 ALTER TABLE articles ADD FULLTEXT INDEX idx_title_content (title, content); -- 全文检索 SELECT * FROM articles WHERE MATCH(title, content) AGAINST('MySQL索引' IN NATURAL LANGUAGE MODE); -
适用场景:博客内容搜索、商品描述检索等文本匹配场景,比
LIKE '%关键词%'效率高 10 倍以上
3. 空间索引
-
结构原理:基于 R 树 / 四叉树,对地理空间数据类型(GEOMETRY、POINT、POLYGON)建立索引。
-
核心功能:支持空间关系查询(如包含、距离计算)
-
使用语法:
sql
-- 创建空间索引 ALTER TABLE shops ADD SPATIAL INDEX idx_location (location); -- 查询1公里内的店铺 SELECT * FROM shops WHERE ST_Distance_Sphere(location, POINT(116.403874, 39.914885)) < 1000; -
适用场景:地图应用(外卖配送范围、附近景点推荐)
(三) 聚簇索引与二级索引(InnoDB 索引体系)
-
聚簇索引:仅 InnoDB 支持,即主键索引,叶子节点存完整数据行,是 InnoDB 的 “主索引”,所有其他索引都依赖聚簇索引。
-
二级索引:除主键索引外的所有索引(唯一索引、普通索引、联合索引、前缀索引),叶子节点存主键值,查询需 “回表”(通过主键查聚簇索引获完整数据)。
-
实战对比:
索引类型 属于聚簇 / 二级索引 叶子节点内容 查询示例 I/O 次数(1 亿数据) 主键索引(InnoDB) 聚簇索引 完整数据行 SELECT * WHERE id=100863 次(无需回表) 唯一索引(username) 二级索引 主键值(如 id=10086) SELECT * WHERE username='zhangsan'6 次(需回表) 联合索引(user_id, create_time) 二级索引 主键值(如 id=5000) SELECT * WHERE user_id=13 AND create_time>'2024-01-01'6 次(需回表)
五、索引实战:设计、优化与避坑
(一)索引设计最佳实践
1. 主键设计原则(结合存储引擎差异)
-
InnoDB 表:优先自增 INT/BIGINT,保证聚簇索引有序插入,避免页分裂;若需业务字段唯一,用 “自增主键 + 唯一索引” 组合:
sql
CREATE TABLE innodb_users ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '自增聚簇主键', user_no VARCHAR(20) NOT NULL COMMENT '业务唯一编号', UNIQUE INDEX idx_unique_user_no (user_no) -- 业务字段用唯一索引 ) ENGINE=InnoDB; -
MyISAM 表:主键类型无严格限制(因无聚簇特性),但仍建议用自增 INT(减少索引碎片)
-
拒绝 UUID/GUID:无序性导致索引页频繁分裂,性能下降 30% 以上;拒绝长业务字段(如手机号),避免索引空间浪费。
2. 联合索引字段顺序
- 高选择性字段放左列:如
user_id(选择性 0.9)放create_time(选择性 0.3)左侧,减少后续扫描数据量 - 高频过滤字段放左列:如高频查询
WHERE user_id=13 AND status=1,则user_id在前,status在后 - 覆盖查询字段:将
SELECT字段加入索引,避免回表(如SELECT user_id, amount则建idx_user_amount(user_id, amount))
3. 覆盖索引:InnoDB 二级索引优化核心(含唯一索引示例)
-
定义:查询字段全部包含在索引中,无需回表查主键索引。
-
实战示例:
sql
-- 1. 唯一索引覆盖查询(查询id+username,无需回表) SELECT id, username FROM users WHERE username='zhangsan'; -- 索引:idx_unique_username(username为索引键,叶子存主键id) -- 执行:直接从唯一索引获id+username,1亿数据3次I/O(原需6次) -- 2. 联合索引覆盖查询(查询amount,无需回表) SELECT amount FROM orders WHERE user_id=13 AND create_time>'2024-01-01'; -- 索引:idx_combo_user_time_amount(user_id, create_time, amount) -- 执行:查询字段amount在索引中,1亿数据3次I/O(原需6次)
(二)索引失效场景全清单(附解决方案)
| 失效场景 | 示例 SQL | 失效原因 | 解决方案 |
|---|---|---|---|
| 索引列用函数 / 运算 | WHERE YEAR(create_time)=2024 | 函数破坏索引有序性 | 改为WHERE create_time BETWEEN '2024-01-01' AND '2024-12-31' |
| 隐式类型转换 | WHERE user_id='13'(user_id 为 INT) | 字符串转 INT 触发全表扫描 | 保证类型一致(WHERE user_id=13) |
| 左模糊查询 | WHERE name LIKE '%john' | 左模糊无法利用索引有序性 | 改用右模糊(LIKE 'john%')或全文索引 |
| OR 连接非索引列 | WHERE user_id=13 OR amount>150(amount 无索引) | OR 需同时扫描索引与非索引列 | 给 amount 加索引,或拆分为两个查询 |
| 使用 NOT IN/!=/IS NOT NULL | WHERE status!=1 | 范围过大,索引过滤效率低 | 改用IN(如WHERE status IN (0,2,3)) |
| JOIN 关联字段不匹配 | orders.user_id(INT)JOIN users.id(VARCHAR) | 类型转换导致索引失效 | 保证关联字段类型与字符集一致 |
(三)索引代价与平衡
索引并非越多越好,需平衡查询性能与写入成本:
1. 空间代价
- 1 亿行数据的 INT 主键索引约占 400MB(1 亿 ×4 字节),VARCHAR (50) 二级索引约占 2GB
- 单表索引数量建议控制在 5 个以内,避免索引文件超过数据文件体积
2. 写入代价
INSERT/UPDATE/DELETE操作需同步更新所有索引,具体影响如下:
| 操作类型 | 索引影响 | 性能损耗示例 |
|---|---|---|
| INSERT | 所有索引树插入新节点,可能触发页分裂 | 10 个索引比 1 个索引慢 5 倍以上 |
| UPDATE | 若更新索引字段,需删除旧索引项并插入新项 | 索引字段更新比非索引字段慢 3 倍 |
| DELETE | 标记索引项为删除,后续需碎片整理 | 大量删除后索引碎片率达 30%+ |
3. 平衡建议
- 写入频繁表(如订单表、日志表):仅保留 1-2 个核心查询索引
- 只读表(如报表表、历史数据表):可适当增加索引
- 定期清理无用索引:通过
sys.schema_unused_indexes查看 30 天未使用的索引,果断删除
(四)用 EXPLAIN 验证索引有效性
EXPLAIN是判断索引是否生效的核心工具,关键字段解读如下:
sql
EXPLAIN SELECT user_id, amount FROM orders WHERE user_id=13;
| 字段 | 含义 | 理想值 |
|---|---|---|
| type | 索引使用类型 | ref(等值查询)、range(范围查询) |
| key | 实际使用的索引名称 | 非 NULL,且为设计的目标索引 |
| rows | 预估扫描行数 | 越小越好(如 < 100) |
| Extra | 额外执行信息 | Using index(覆盖索引)、Using where(无多余排序) |
常见异常情况:
type=ALL:全表扫描,索引未生效Extra=Using filesort:需额外排序,索引未包含排序字段Extra=Using temporary:需创建临时表,通常因 GROUP BY/ORDER BY 字段无索引
六、知识体系总结:MySQL 索引全景图
plaintext
MySQL索引体系
├─ 基础理论
│ ├─ 本质:空间换时间,减少磁盘I/O
│ ├─ 核心指标:树高=I/O次数,10亿数据4次I/O
│ └─ 价值:全表扫描→结构化查找,效率提升百万倍
├─ 核心结构
│ └─ B+树:内部存key+叶子存data+双向链表,磁盘存储最优解
├─ 存储引擎实现
│ ├─ InnoDB:主键索引=聚簇索引(叶子存数据)+ 二级索引(需回表)
│ └─ MyISAM:主键索引=普通B+树(叶子存指针)+ 二级索引(同结构)
├─ 索引类型全谱
│ ├─ B+树家族(核心)
│ │ ├─ 主键索引:所有引擎支持,InnoDB为聚簇型,MyISAM为普通型
│ │ ├─ 唯一索引:支持多NULL,保证唯一性+优化查询
│ │ ├─ 普通索引:无约束,仅优化单条件查询
│ │ ├─ 联合索引:多字段组合,遵循最左前缀原则
│ │ ├─ 前缀索引:长字符优化,需计算选择性
│ │ └─ 二级索引:InnoDB中非聚簇索引,需回表
│ └─ 特殊索引
│ ├─ 哈希索引:等值快,不支持范围
│ ├─ 全文索引:倒排结构,文本检索
│ └─ 空间索引:R树/四叉树,地理查询
├─ 实战设计
│ ├─ 主键:InnoDB用自增INT+唯一索引,MyISAM避免长字段
│ ├─ 联合索引:高选择性+高频字段放左,覆盖查询字段
│ └─ 避坑:避免函数运算、隐式转换、左模糊
└─ 平衡优化
├─ 代价控制:单表索引≤5个,写入表少建索引
├─ 验证工具:EXPLAIN看type/key/Extra
└─ 定期维护:删无用索引,清碎片
终极结论
MySQL 索引的核心是B + 树结构与场景化选择,需重点关注存储引擎差异与约束细节:
- 主键索引是所有引擎的基础,但 InnoDB 的聚簇特性是其查询效率核心,MyISAM 无此特性,设计时需针对性选型;
- 唯一索引支持多个 NULL 值,需修正 “仅允许 1 个 NULL” 的认知,避免业务逻辑错误;
- 日常查询优先用 B + 树索引家族,通过覆盖索引优化 InnoDB 二级查询,特殊场景(文本、地理)用非 B + 树索引;
- 索引设计需平衡性能与代价,通过 EXPLAIN 验证有效性,定期维护优化。