索引:数据库中用于提高查询速度的数据结构。
东巴文理解:
索引 = 书籍目录
书籍目录 → 快速定位内容
数据库索引 → 快速定位数据
优点:
缺点:
特点:
结构示意:
[根节点]
/ | \
[中间节点] [中间节点] [中间节点]
/ | \
[叶子节点] [叶子节点] [叶子节点]
↓ ↓ ↓
[数据] [数据] [数据]
示例:
-- 创建B+Tree索引
CREATE INDEX idx_name ON employees(name);
-- 查看索引
SHOW INDEX FROM employees;
特点:
示例:
-- Memory引擎支持Hash索引
CREATE TABLE test_hash (
id INT PRIMARY KEY,
name VARCHAR(50),
INDEX idx_name USING HASH (name)
) ENGINE=MEMORY;
特点:
示例:
-- 创建全文索引
CREATE FULLTEXT INDEX idx_content ON articles(content);
-- 使用全文索引
SELECT * FROM articles
WHERE MATCH(content) AGAINST('数据库');
特点:
示例:
-- 创建空间索引
CREATE TABLE locations (
id INT PRIMARY KEY,
name VARCHAR(50),
point POINT,
SPATIAL INDEX idx_point (point)
);
-- 插入空间数据
INSERT INTO locations VALUES
(1, '北京', POINT(116.4074, 39.9042)),
(2, '上海', POINT(121.4737, 31.2304));
特点:
示例:
-- 创建主键索引
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50)
);
-- 或
CREATE TABLE users (
id INT,
name VARCHAR(50),
PRIMARY KEY (id)
);
特点:
示例:
-- 创建唯一索引
CREATE UNIQUE INDEX idx_email ON users(email);
-- 或
CREATE TABLE users (
id INT PRIMARY KEY,
email VARCHAR(100) UNIQUE
);
特点:
示例:
-- 创建普通索引
CREATE INDEX idx_name ON users(name);
-- 或
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50),
INDEX idx_name (name)
);
特点:
示例:
-- 创建组合索引
CREATE INDEX idx_name_age ON users(name, age);
-- 使用组合索引
SELECT * FROM users WHERE name = '张三'; -- 使用索引
SELECT * FROM users WHERE name = '张三' AND age = 25; -- 使用索引
SELECT * FROM users WHERE age = 25; -- 不使用索引
✅ WHERE子句频繁使用的列 ✅ JOIN连接的列 ✅ ORDER BY排序的列 ✅ GROUP BY分组的列 ✅ 区分度高的列
示例:
-- 区分度高的列适合创建索引
SELECT
COUNT(DISTINCT name) / COUNT(*) AS name_selectivity,
COUNT(DISTINCT gender) / COUNT(*) AS gender_selectivity
FROM users;
-- name_selectivity: 0.95(高区分度,适合索引)
-- gender_selectivity: 0.5(低区分度,不适合索引)
❌ 数据量小的表 ❌ 频繁更新的列 ❌ 区分度低的列 ❌ 很少使用的列
示例:
-- 不适合创建索引的列
CREATE TABLE logs (
id INT PRIMARY KEY,
level VARCHAR(10), -- 区分度低(INFO/WARN/ERROR)
message TEXT, -- 长文本
created_at TIMESTAMP
);
-- level区分度低,不适合单独索引
-- message是长文本,不适合索引
组合索引:按照定义顺序,从左到右依次匹配。
示例:
-- 创建组合索引
CREATE INDEX idx_a_b_c ON test(a, b, c);
-- 使用索引的情况
WHERE a = 1 -- ✅ 使用索引
WHERE a = 1 AND b = 2 -- ✅ 使用索引
WHERE a = 1 AND b = 2 AND c = 3 -- ✅ 使用索引
-- 不使用索引的情况
WHERE b = 2 -- ❌ 不使用索引
WHERE c = 3 -- ❌ 不使用索引
WHERE b = 2 AND c = 3 -- ❌ 不使用索引
-- 部分使用索引
WHERE a = 1 AND c = 3 -- ✅ 只使用a列索引
范围查询后的列不使用索引:
-- 创建组合索引
CREATE INDEX idx_a_b_c ON test(a, b, c);
-- 范围查询
WHERE a = 1 AND b > 2 AND c = 3 -- ✅ 使用a和b,不使用c
语法格式:
CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX 索引名
ON 表名(列名[(长度)] [ASC|DESC]);
示例:
-- 创建普通索引
CREATE INDEX idx_name ON employees(name);
-- 创建唯一索引
CREATE UNIQUE INDEX idx_email ON employees(email);
-- 创建组合索引
CREATE INDEX idx_dept_salary ON employees(dept_id, salary);
-- 创建前缀索引
CREATE INDEX idx_name_prefix ON employees(name(10));
语法格式:
ALTER TABLE 表名 ADD [索引类型] INDEX 索引名(列名);
示例:
-- 添加主键
ALTER TABLE employees ADD PRIMARY KEY (id);
-- 添加唯一索引
ALTER TABLE employees ADD UNIQUE INDEX idx_email (email);
-- 添加普通索引
ALTER TABLE employees ADD INDEX idx_name (name);
语法格式:
DROP INDEX 索引名 ON 表名;
示例:
-- 删除索引
DROP INDEX idx_name ON employees;
语法格式:
ALTER TABLE 表名 DROP INDEX 索引名;
示例:
-- 删除索引
ALTER TABLE employees DROP INDEX idx_name;
-- 删除主键
ALTER TABLE employees DROP PRIMARY KEY;
语法格式:
SHOW INDEX FROM 表名;
示例:
-- 查看索引
SHOW INDEX FROM employees;
输出说明:
| 字段 | 说明 |
|---|---|
| Table | 表名 |
| Non_unique | 是否非唯一 |
| Key_name | 索引名 |
| Seq_in_index | 索引中的列序号 |
| Column_name | 列名 |
| Collation | 排序方式 |
| Cardinality | 基数(区分度) |
| Sub_part | 前缀长度 |
| Null | 是否允许NULL |
错误示例:
-- 索引失效
SELECT * FROM employees WHERE YEAR(created_at) = 2024;
-- 优化后
SELECT * FROM employees
WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';
错误示例:
-- 索引失效
SELECT * FROM employees WHERE salary * 12 > 100000;
-- 优化后
SELECT * FROM employees WHERE salary > 100000 / 12;
错误示例:
-- 字符串列,传入数字,索引失效
SELECT * FROM employees WHERE phone = 13800138000;
-- 优化后
SELECT * FROM employees WHERE phone = '13800138000';
错误示例:
-- 索引可能失效
SELECT * FROM employees WHERE status != 0;
-- 优化后
SELECT * FROM employees WHERE status IN (1, 2, 3);
错误示例:
-- 索引可能失效
SELECT * FROM employees WHERE name = '张三' OR age = 25;
-- 优化后
SELECT * FROM employees WHERE name = '张三'
UNION
SELECT * FROM employees WHERE age = 25;
错误示例:
-- 索引失效(前缀模糊)
SELECT * FROM employees WHERE name LIKE '%张%';
-- 使用索引(后缀模糊)
SELECT * FROM employees WHERE name LIKE '张%';
覆盖索引:查询所需的所有列都在索引中,不需要回表。
示例:
-- 创建组合索引
CREATE INDEX idx_name_age ON employees(name, age);
-- 使用覆盖索引
SELECT name, age FROM employees WHERE name = '张三';
-- 查看执行计划
EXPLAIN SELECT name, age FROM employees WHERE name = '张三';
Extra列显示Using index:表示使用覆盖索引。
索引下推(ICP):在索引遍历过程中,对索引中包含的字段进行判断,减少回表次数。
示例:
-- 创建组合索引
CREATE INDEX idx_name_age ON employees(name, age);
-- 查询
SELECT * FROM employees WHERE name LIKE '张%' AND age = 25;
无ICP:
有ICP:
查看ICP:
EXPLAIN SELECT * FROM employees WHERE name LIKE '张%' AND age = 25;
Extra列显示Using index condition:表示使用索引下推。
语法格式:
EXPLAIN SELECT语句;
示例:
EXPLAIN SELECT * FROM employees WHERE name = '张三';
id:查询标识符
select_type:查询类型
| 类型 | 说明 |
|---|---|
| SIMPLE | 简单查询 |
| PRIMARY | 最外层查询 |
| SUBQUERY | 子查询 |
| DERIVED | 派生表 |
| UNION | 联合查询 |
type:访问类型(性能从好到差)
| 类型 | 说明 |
|---|---|
| system | 系统表 |
| const | 常量 |
| eq_ref | 唯一索引 |
| ref | 非唯一索引 |
| range | 范围查询 |
| index | 索引扫描 |
| ALL | 全表扫描 |
key:使用的索引
rows:预估扫描行数
Extra:额外信息
| 信息 | 说明 |
|---|---|
| Using index | 覆盖索引 |
| Using where | WHERE过滤 |
| Using temporary | 使用临时表 |
| Using filesort | 文件排序 |
| Using index condition | 索引下推 |
示例1:全表扫描优化
-- 查看执行计划
EXPLAIN SELECT * FROM employees WHERE name = '张三';
-- type: ALL(全表扫描)
-- 创建索引
CREATE INDEX idx_name ON employees(name);
-- 再次查看
EXPLAIN SELECT * FROM employees WHERE name = '张三';
-- type: ref(索引查找)
示例2:文件排序优化
-- 查看执行计划
EXPLAIN SELECT * FROM employees ORDER BY salary;
-- Extra: Using filesort(文件排序)
-- 创建索引
CREATE INDEX idx_salary ON employees(salary);
-- 再次查看
EXPLAIN SELECT * FROM employees ORDER BY salary;
-- Extra: Using index(使用索引)
查看配置:
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';
开启慢查询日志:
-- 临时开启
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1; -- 超过1秒记录
-- 永久开启(修改配置文件)
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
使用mysqldumpslow:
# 查看慢查询日志
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
# 参数说明
-s t:按查询时间排序
-t 10:显示前10条
使用pt-query-digest:
# 安装Percona Toolkit
# 分析慢查询日志
pt-query-digest /var/log/mysql/slow.log
只查询需要的列:
-- 不推荐
SELECT * FROM employees;
-- 推荐
SELECT id, name FROM employees;
使用LIMIT:
-- 限制返回行数
SELECT * FROM employees LIMIT 100;
**避免SELECT ***:
-- 不推荐
SELECT * FROM employees WHERE name = '张三';
-- 推荐
SELECT id, name, salary FROM employees WHERE name = '张三';
选择合适的数据类型:
-- 不推荐
CREATE TABLE users (
id INT,
name VARCHAR(255),
age INT,
gender VARCHAR(10)
);
-- 推荐
CREATE TABLE users (
id INT UNSIGNED,
name VARCHAR(50),
age TINYINT UNSIGNED,
gender ENUM('男', '女')
);
避免NULL值:
-- 不推荐
CREATE TABLE users (
id INT,
name VARCHAR(50)
);
-- 推荐
CREATE TABLE users (
id INT NOT NULL,
name VARCHAR(50) NOT NULL DEFAULT ''
);
定期分析和优化表:
-- 分析表
ANALYZE TABLE employees;
-- 优化表
OPTIMIZE TABLE employees;
查看索引使用情况:
SELECT
TABLE_NAME,
INDEX_NAME,
CARDINALITY,
SEQ_IN_INDEX
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = 'database_name'
ORDER BY TABLE_NAME, INDEX_NAME;
问题查询:
SELECT * FROM orders
WHERE YEAR(created_at) = 2024
AND MONTH(created_at) = 1
AND status = 1;
分析:
EXPLAIN SELECT * FROM orders
WHERE YEAR(created_at) = 2024
AND MONTH(created_at) = 1
AND status = 1;
优化方案:
-- 1. 创建组合索引
CREATE INDEX idx_status_created ON orders(status, created_at);
-- 2. 修改查询条件
SELECT * FROM orders
WHERE created_at >= '2024-01-01'
AND created_at < '2024-02-01'
AND status = 1;
问题查询:
SELECT * FROM orders ORDER BY id LIMIT 100000, 10;
优化方案:
-- 方案1:使用覆盖索引
SELECT * FROM orders o
INNER JOIN (
SELECT id FROM orders ORDER BY id LIMIT 100000, 10
) tmp ON o.id = tmp.id;
-- 方案2:使用游标
SELECT * FROM orders WHERE id > 100000 ORDER BY id LIMIT 10;
问题查询:
SELECT * FROM orders o
INNER JOIN users u ON o.user_id = u.id
WHERE o.status = 1;
优化方案:
-- 1. 创建索引
CREATE INDEX idx_user_id ON orders(user_id);
CREATE INDEX idx_status ON orders(status);
-- 2. 使用小表驱动大表
SELECT * FROM (
SELECT * FROM orders WHERE status = 1
) o
INNER JOIN users u ON o.user_id = u.id;
✅ 索引提高查询速度,但降低写入速度 ✅ 选择合适的索引类型和列 ✅ 遵循最左前缀原则 ✅ 避免索引失效的情况 ✅ 使用EXPLAIN分析查询
完成本章学习后,请确认您能够: