1. MySQL索引概述
索引是MySQL数据库中用于加速数据检索的关键数据结构,它类似于书籍的目录,能够帮助数据库引擎快速定位到表中的特定数据行。在数据量大的表中,合理的索引设计可以显著提升查询性能,而不当的索引则可能导致性能下降甚至更糟。
索引本质上是一种特殊的数据结构,它保存了表中一列或多列的值,并按照特定顺序组织这些值,以便快速查找。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. MySQL索引分类详解
2.1 按数据结构分类
2.1.1 B-Tree索引
B-Tree(平衡树)是MySQL中最常用的索引类型,InnoDB和MyISAM存储引擎都支持。它的特点包括:
- 所有值按顺序存储
- 每个叶子节点到根节点的距离相同
- 适合全键值、键值范围或键前缀查找
sql复制-- 创建B-Tree索引示例
CREATE INDEX idx_name ON users(name);
2.1.2 Hash索引
Hash索引基于哈希表实现,只有精确匹配所有列的查询才有效。其特点:
- 仅支持"="、"IN"等精确查询
- 不支持范围查询
- 查询效率极高,O(1)时间复杂度
- Memory引擎默认索引类型
sql复制-- 创建Hash索引示例(仅Memory引擎支持)
CREATE INDEX idx_email ON users(email) USING HASH;
2.1.3 全文索引(Full-Text)
专门用于文本内容的搜索,支持自然语言搜索和布尔搜索:
- 仅MyISAM和InnoDB(5.6+)支持
- 适用于CHAR、VARCHAR和TEXT列
- 使用MATCH AGAINST语法查询
sql复制-- 创建全文索引示例
CREATE FULLTEXT INDEX idx_content ON articles(content);
2.1.4 R-Tree索引
用于空间数据类型的索引,支持地理数据存储和查询:
- 仅MyISAM支持
- 适用于GEOMETRY数据类型
- 使用GIS相关函数查询
sql复制-- 创建空间索引示例
CREATE SPATIAL INDEX idx_location ON maps(coordinates);
2.2 按物理存储分类
2.2.1 聚簇索引(Clustered Index)
InnoDB的主键索引就是聚簇索引,特点:
- 索引和数据存储在一起
- 每个表只能有一个聚簇索引
- 主键自动成为聚簇索引
- 非主键索引(二级索引)会引用主键值
2.2.2 非聚簇索引(Non-Clustered Index)
也称为二级索引,特点:
- 索引和数据分开存储
- 一个表可以有多个非聚簇索引
- InnoDB中非聚簇索引存储的是主键值
- MyISAM中存储的是数据行的物理地址
2.3 按逻辑功能分类
2.3.1 主键索引(Primary Key)
- 唯一标识表中的每一行
- 不允许NULL值
- 自动创建聚簇索引(InnoDB)
- 一个表只能有一个主键
sql复制-- 创建主键索引
ALTER TABLE users ADD PRIMARY KEY (id);
2.3.2 唯一索引(Unique Key)
- 确保索引列的值唯一
- 允许NULL值(但只能有一个NULL)
- 可加速查询
- 可防止数据重复
sql复制-- 创建唯一索引
CREATE UNIQUE INDEX idx_email ON users(email);
2.3.3 普通索引(Index)
最基本的索引类型:
- 无唯一性限制
- 仅用于加速查询
- 可包含NULL值
sql复制-- 创建普通索引
CREATE INDEX idx_name ON users(name);
2.3.4 复合索引(Composite Index)
在多个列上建立的索引:
- 遵循最左前缀原则
- 列顺序影响索引效果
- 可减少单列索引数量
sql复制-- 创建复合索引
CREATE INDEX idx_name_age ON users(name, age);
2.3.5 前缀索引(Prefix Index)
对列的前N个字符建立索引:
- 适用于长文本列
- 可节省索引空间
- 可能降低选择性
sql复制-- 创建前缀索引(只索引前10个字符)
CREATE INDEX idx_name_prefix ON users(name(10));
3. 索引选择与优化策略
3.1 索引选择原则
-
高选择性原则:选择区分度高的列建索引
sql复制-- 计算列的选择性 SELECT COUNT(DISTINCT column_name)/COUNT(*) FROM table_name; -
最左前缀原则:复合索引中,查询条件需包含最左列
sql复制-- 对于INDEX(a,b,c),以下查询能使用索引 WHERE a = 1 AND b = 2 AND c = 3 WHERE a = 1 AND b = 2 WHERE a = 1 -
覆盖索引原则:索引包含查询所需的所有字段
sql复制-- 创建覆盖索引 CREATE INDEX idx_covering ON orders(order_date, customer_id, amount); -- 查询可使用覆盖索引 SELECT order_date, customer_id, amount FROM orders WHERE order_date > '2023-01-01';
3.2 索引优化技巧
-
EXPLAIN分析:使用EXPLAIN查看查询执行计划
sql复制EXPLAIN SELECT * FROM users WHERE name = 'John'; -
索引合并优化:MySQL有时会合并多个索引
sql复制-- 可能合并idx_name和idx_age索引 SELECT * FROM users WHERE name = 'John' OR age = 30; -
索引提示:强制使用特定索引
sql复制SELECT * FROM users USE INDEX (idx_name) WHERE name = 'John'; -
索引统计信息更新:定期分析表
sql复制ANALYZE TABLE users;
4. 常见索引问题与解决方案
4.1 索引失效场景
-
使用函数或表达式
sql复制-- 索引失效 SELECT * FROM users WHERE YEAR(create_time) = 2023; -- 优化后(使用范围查询) SELECT * FROM users WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31'; -
隐式类型转换
sql复制-- 假设phone是varchar类型,索引失效 SELECT * FROM users WHERE phone = 13800138000; -- 优化后 SELECT * FROM users WHERE phone = '13800138000'; -
使用OR条件
sql复制-- 除非所有列都有索引,否则可能失效 SELECT * FROM users WHERE name = 'John' OR age = 30;
4.2 索引维护建议
-
定期检查未使用索引
sql复制-- 查询schema中未使用的索引 SELECT * FROM sys.schema_unused_indexes; -
监控索引使用情况
sql复制-- 查看索引使用统计 SELECT * FROM sys.schema_index_statistics; -
避免过度索引:每个额外索引都会增加写操作开销
-
定期优化表:特别是频繁更新的表
sql复制OPTIMIZE TABLE users;
5. 高级索引技术
5.1 自适应哈希索引
InnoDB特有的特性:
- 自动为频繁访问的索引页构建哈希索引
- 完全自动管理,无需配置
- 可通过参数调整灵敏度
sql复制SHOW VARIABLES LIKE 'innodb_adaptive_hash_index%';
5.2 倒排索引
全文索引使用的技术:
- 存储单词到文档的映射
- 支持自然语言搜索
- 支持布尔搜索模式
5.3 函数索引
MySQL 8.0+支持:
- 基于表达式或函数结果创建索引
- 解决函数导致索引失效问题
sql复制-- 创建函数索引 CREATE INDEX idx_year ON users((YEAR(create_time)));
5.4 不可见索引
MySQL 8.0+特性:
- 索引对优化器不可见
- 测试删除索引的影响
sql复制-- 创建不可见索引 CREATE INDEX idx_test ON users(name) INVISIBLE; -- 切换索引可见性 ALTER TABLE users ALTER INDEX idx_test VISIBLE;
6. 存储引擎与索引
6.1 InnoDB索引特点
- 聚簇索引组织表
- 二级索引包含主键值
- 支持自适应哈希索引
- 支持行级锁
6.2 MyISAM索引特点
- 非聚簇索引结构
- 索引和数据分离存储
- 支持全文索引
- 仅支持表级锁
6.3 Memory引擎索引
- 默认使用哈希索引
- 可指定使用B-Tree索引
- 数据存储在内存中
- 表级锁
7. 索引设计实战案例
7.1 电商系统索引设计
sql复制-- 商品表索引设计
CREATE TABLE products (
id BIGINT PRIMARY KEY,
category_id INT,
name VARCHAR(100),
price DECIMAL(10,2),
stock INT,
status TINYINT,
created_at DATETIME,
INDEX idx_category (category_id),
INDEX idx_name (name),
INDEX idx_price_status (price, status),
INDEX idx_created (created_at)
);
-- 订单表索引设计
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
user_id BIGINT,
order_no VARCHAR(32),
status TINYINT,
amount DECIMAL(12,2),
created_at DATETIME,
UNIQUE INDEX idx_order_no (order_no),
INDEX idx_user_status (user_id, status),
INDEX idx_created_status (created_at, status)
);
7.2 社交系统索引设计
sql复制-- 用户关系表索引设计
CREATE TABLE user_relations (
user_id BIGINT,
friend_id BIGINT,
relation_type TINYINT,
created_at DATETIME,
PRIMARY KEY (user_id, friend_id),
INDEX idx_friend (friend_id),
INDEX idx_type (relation_type)
);
-- 动态表索引设计
CREATE TABLE posts (
id BIGINT PRIMARY KEY,
user_id BIGINT,
visibility TINYINT,
created_at DATETIME,
INDEX idx_user_visibility (user_id, visibility),
INDEX idx_visibility_created (visibility, created_at)
);
8. 索引监控与维护
8.1 索引监控命令
sql复制-- 查看索引基本信息
SHOW INDEX FROM users;
-- 查看索引使用情况
SELECT * FROM sys.schema_index_statistics;
-- 查看冗余索引
SELECT * FROM sys.schema_redundant_indexes;
8.2 索引维护操作
sql复制-- 重建索引(InnoDB)
ALTER TABLE users DROP INDEX idx_name;
ALTER TABLE users ADD INDEX idx_name (name);
-- 优化表(重建表并整理索引)
OPTIMIZE TABLE users;
-- 分析表(更新索引统计信息)
ANALYZE TABLE users;
8.3 索引大小监控
sql复制-- 查看表和索引大小
SELECT
table_name,
index_name,
stat_value * @@innodb_page_size / 1024 / 1024 AS size_mb
FROM
mysql.innodb_index_stats
WHERE
stat_name = 'size'
AND database_name = 'your_db'
ORDER BY
size_mb DESC;
