【MySQL成神之路】MySQL索引相关介绍
1 相关理论介绍
一、索引基础概念
二、索引类型
1. 按数据结构分类
2. 按功能分类
三、索引数据结构原理
B+树索引特点:
哈希索引特点:
四、索引使用原则
1. 创建索引原则
2. 避免索引失效情况
五、索引优化策略
六、索引维护与管理
七、特殊索引注意事项
八、索引与存储引擎
2、代码操作示例
一、索引创建方法
1. 创建表时定义索引
2. 在已有表上创建索引
二、索引使用方法
1. 基本查询使用索引
2. 覆盖索引查询
三、索引优化方法
1. 索引设计原则
2. 索引使用优化
3. 索引维护优化
四、综合示例
1 相关理论介绍
一、索引基础概念
索引是MySQL中用于加速查询的一种数据结构,类似于书籍的目录。它通过建立额外的数据结构来快速定位数据,避免全表扫描。索引本质上是一种有序的数据结构,MySQL主要使用B+树作为索引结构。
二、索引类型
1. 按数据结构分类
- B+树索引:MySQL最常用的索引类型,适合范围查询和排序
- 哈希索引:Memory引擎默认索引类型,适合等值查询但不支持范围查询
- 全文索引:用于全文搜索,MyISAM和InnoDB都支持
- 空间索引:用于地理空间数据,MyISAM支持
2. 按功能分类
- 普通索引:最基本的索引类型,无特殊限制
- 唯一索引:索引列值必须唯一但允许NULL值
- 主键索引:特殊的唯一索引,不允许NULL值
- 复合索引:多个列组合的索引
- 前缀索引:对字符列前N个字符建立的索引
三、索引数据结构原理
B+树索引特点:
- 所有数据都存储在叶子节点,非叶子节点只存储键值
- 叶子节点通过指针连接形成链表,便于范围查询
- 树高度通常为3-4层,能支持千万级数据高效查询
- 查询时间复杂度为O(log n)
哈希索引特点:
- 基于哈希表实现,查询时间复杂度为O(1)
- 只支持等值查询(=, IN),不支持范围查询(>, <, BETWEEN)
- 不支持排序操作
四、索引使用原则
1. 创建索引原则
- 为常用于WHERE条件的列创建索引
- 为JOIN连接的列创建索引
- 为ORDER BY、GROUP BY的列创建索引
- 选择区分度高的列建立索引
- 使用短索引,特别是对字符串列
- 合理使用复合索引,遵循最左前缀原则
2. 避免索引失效情况
- 在索引列上使用函数或运算
- 使用!=或<>操作符
- 使用OR连接条件(可改为IN)
- 使用前导通配符LIKE '%xxx'
- 隐式类型转换导致索引失效
- 复合索引不遵循最左前缀原则
五、索引优化策略
- 覆盖索引:查询列都包含在索引中,避免回表操作
- 索引下推:MySQL5.6+特性,将WHERE条件下推到存储引擎层过滤
- MRR优化:Multi-Range Read优化,减少随机IO
- ICP优化:Index Condition Pushdown优化
- 使用EXPLAIN分析:查看SQL执行计划,优化索引使用
六、索引维护与管理
- 定期分析表(ANALYZE TABLE)更新索引统计信息
- 定期优化表(OPTIMIZE TABLE)减少碎片
- 监控索引使用情况,删除无用索引
- 避免过多索引,一般不超过表字段数的20%
七、特殊索引注意事项
- 自增主键:InnoDB推荐使用自增列作为主键
- 前缀索引:对长字符串列使用前N个字符建立索引
- NULL值处理:尽量避免NULL值,可为NULL的列需要额外空间
- 外键索引:线上OLTP系统慎用外键
八、索引与存储引擎
-
InnoDB:
- 使用聚簇索引,主键作为聚簇索引
- 二级索引存储主键值
- 支持事务和行级锁
-
MyISAM:
- 使用非聚簇索引,索引和数据分离
- 只支持表级锁
- 支持全文索引
2、代码操作示例
一、索引创建方法
1. 创建表时定义索引
-- 主键索引
CREATE TABLE employee_tbl (emp_id CHAR(9) NOT NULL PRIMARY KEY,emp_name VARCHAR(40) NOT NULL,emp_st_addr VARCHAR(20) NOT NULL,emp_city VARCHAR(15) NOT NULL,emp_st CHAR(2) NOT NULL,emp_zip NUMBER(5) NOT NULL
);-- 多列索引
CREATE TABLE sales (id INT NOT NULL,customer_id INT NOT NULL,amount DECIMAL(10,2),sale_date DATE,PRIMARY KEY (id),INDEX idx_customer_date (customer_id, sale_date)
);
2. 在已有表上创建索引
-- 普通索引
CREATE INDEX idx_name ON employee_tbl(emp_name);-- 唯一索引
CREATE UNIQUE INDEX idx_zip ON employee_tbl(emp_zip);-- 前缀索引(针对字符串列)
CREATE INDEX idx_city_prefix ON employee_tbl(emp_city(5));
二、索引使用方法
1. 基本查询使用索引
-- 使用主键查询(自动使用索引)
SELECT * FROM employee_tbl WHERE emp_id = '12345';-- 使用普通索引列查询
SELECT * FROM employee_tbl WHERE emp_name = 'John Doe';-- 使用多列索引
SELECT * FROM sales WHERE customer_id = 100 AND sale_date = '2025-05-23';
2. 覆盖索引查询
-- 如果索引包含所有查询字段,可以避免回表
CREATE INDEX idx_covering ON sales(customer_id, sale_date, amount);-- 查询只使用索引列
SELECT customer_id, sale_date FROM sales
WHERE customer_id = 100 AND sale_date BETWEEN '2025-05-21' AND '2025-05-23';
三、索引优化方法
1. 索引设计原则
优先使用数值类型索引:数值比较比字符串快
-- 不推荐
CREATE INDEX idx_bad ON table(phone_str);-- 推荐:将字符串转为数字
CREATE INDEX idx_good ON table(CAST(phone_str AS UNSIGNED));
合理使用ENUM/SET:
-- 对于有限可能值的字段
ALTER TABLE employee_tbl
ADD COLUMN gender ENUM('M','F') NOT NULL COMMENT '性别';
避免NULL字段:
-- 不推荐
ALTER TABLE employee_tbl ADD COLUMN middle_name VARCHAR(20) NULL;-- 推荐
ALTER TABLE employee_tbl ADD COLUMN middle_name VARCHAR(20) NOT NULL DEFAULT '';
2. 索引使用优化
避免索引列运算:
-- 不推荐(索引失效)
SELECT * FROM sales WHERE YEAR(sale_date) = 2025;-- 推荐
SELECT * FROM sales WHERE sale_date BETWEEN '2025-05-23' AND '2025-05-23';
合理使用前缀索引:
-- 对长字符串列使用前缀索引
CREATE INDEX idx_name_prefix ON employee_tbl(emp_name(10));
多列索引顺序:
-- 选择性高的列在前
CREATE INDEX idx_optimal ON sales(sale_date, customer_id);
3. 索引维护优化
定期分析表:
ANALYZE TABLE employee_tbl;
删除未使用索引:
-- 通过性能Schema或慢查询日志识别未使用索引
DROP INDEX idx_unused ON employee_tbl;
处理索引碎片:
OPTIMIZE TABLE employee_tbl;
四、综合示例
-- 创建优化后的表结构
CREATE TABLE optimized_employee (id INT UNSIGNED NOT NULL AUTO_INCREMENT,emp_code CHAR(8) NOT NULL COMMENT '员工编码',name VARCHAR(30) NOT NULL,department ENUM('IT','HR','Finance','Sales') NOT NULL,join_date DATE NOT NULL,salary DECIMAL(10,2) NOT NULL DEFAULT 0,status TINYINT NOT NULL DEFAULT 1 COMMENT '0-离职 1-在职',PRIMARY KEY (id),UNIQUE KEY uk_emp_code (emp_code),INDEX idx_department_status (department, status),INDEX idx_name (name(10)),INDEX idx_join_date (join_date)
) ENGINE=InnoDB;-- 查询示例(充分利用索引)
-- 1. 使用主键查询
SELECT * FROM optimized_employee WHERE id = 100;-- 2. 使用多列索引
SELECT id, name FROM optimized_employee
WHERE department = 'IT' AND status = 1
ORDER BY join_date DESC;-- 3. 覆盖索引查询
SELECT department, COUNT(*)
FROM optimized_employee
WHERE join_date > '2025-05-23'
GROUP BY department;