MySQL性能优化 #

一、性能优化概述 #

1.1 优化方向 #

text
MySQL性能优化
├── 查询优化
│   ├── SQL语句优化
│   ├── 索引优化
│   └── 执行计划分析
├── 结构优化
│   ├── 表结构设计
│   ├── 数据类型选择
│   └── 范式与反范式
├── 配置优化
│   ├── 内存配置
│   ├── 缓冲区配置
│   └── 连接配置
└── 架构优化
    ├── 读写分离
    ├── 分库分表
    └── 缓存策略

1.2 性能指标 #

指标 说明
QPS 每秒查询数
TPS 每秒事务数
响应时间 查询执行时间
并发数 同时处理的请求数

二、EXPLAIN执行计划 #

2.1 基本用法 #

sql
-- 查看执行计划
EXPLAIN SELECT * FROM users WHERE id = 1;

-- 查看完整执行计划
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE id = 1;

-- MySQL 8.0+ 分析语句
EXPLAIN ANALYZE SELECT * FROM users WHERE id = 1;

2.2 执行计划列说明 #

sql
EXPLAIN SELECT * FROM users WHERE name = 'John';

+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
说明
id 查询标识符
select_type 查询类型
table 表名
type 访问类型
possible_keys 可能使用的索引
key 实际使用的索引
key_len 索引长度
ref 索引比较的列
rows 预估扫描行数
filtered 过滤百分比
Extra 额外信息

2.3 type访问类型 #

sql
-- 从好到差:
-- system > const > eq_ref > ref > range > index > ALL

-- system:单行表
EXPLAIN SELECT * FROM (SELECT 1) t;

-- const:主键或唯一索引
EXPLAIN SELECT * FROM users WHERE id = 1;

-- eq_ref:JOIN时使用主键或唯一索引
EXPLAIN SELECT * FROM users u JOIN orders o ON u.id = o.user_id;

-- ref:非唯一索引
EXPLAIN SELECT * FROM users WHERE name = 'John';

-- range:范围查询
EXPLAIN SELECT * FROM users WHERE age BETWEEN 20 AND 30;

-- index:索引扫描
EXPLAIN SELECT id FROM users;

-- ALL:全表扫描(最差)
EXPLAIN SELECT * FROM users WHERE age + 1 = 25;

2.4 Extra信息 #

sql
-- Using index:覆盖索引
EXPLAIN SELECT id FROM users WHERE id = 1;

-- Using where:WHERE过滤
EXPLAIN SELECT * FROM users WHERE name = 'John';

-- Using temporary:使用临时表
EXPLAIN SELECT DISTINCT name FROM users;

-- Using filesort:文件排序
EXPLAIN SELECT * FROM users ORDER BY name;

-- Using index condition:索引条件下推
EXPLAIN SELECT * FROM users WHERE name LIKE 'J%' AND age = 25;

三、查询优化 #

3.1 避免SELECT * #

sql
-- 不推荐
SELECT * FROM users;

-- 推荐:只查询需要的列
SELECT id, name, email FROM users;

3.2 使用索引 #

sql
-- 确保WHERE条件使用索引
-- 创建索引
CREATE INDEX idx_name ON users(name);

-- 使用索引
SELECT * FROM users WHERE name = 'John';

-- 避免索引失效
-- 错误:使用函数
SELECT * FROM users WHERE YEAR(created_at) = 2024;

-- 正确
SELECT * FROM users WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';

3.3 避免隐式转换 #

sql
-- 错误:字符串与数字比较
SELECT * FROM users WHERE phone = 13800138000;

-- 正确
SELECT * FROM users WHERE phone = '13800138000';

3.4 优化LIKE #

sql
-- 错误:以%开头
SELECT * FROM users WHERE name LIKE '%John%';

-- 正确:不以%开头
SELECT * FROM users WHERE name LIKE 'John%';

-- 必须使用%开头时,考虑全文索引
CREATE FULLTEXT INDEX ft_name ON users(name);
SELECT * FROM users WHERE MATCH(name) AGAINST('John');

3.5 优化OR #

sql
-- 错误:OR可能导致索引失效
SELECT * FROM users WHERE name = 'John' OR age = 25;

-- 正确:使用UNION
SELECT * FROM users WHERE name = 'John'
UNION
SELECT * FROM users WHERE age = 25;

3.6 优化分页 #

sql
-- 传统分页(大数据量时慢)
SELECT * FROM users ORDER BY id LIMIT 10000, 10;

-- 优化方式1:使用子查询
SELECT * FROM users u
JOIN (SELECT id FROM users ORDER BY id LIMIT 10000, 10) t
ON u.id = t.id;

-- 优化方式2:使用WHERE条件
SELECT * FROM users WHERE id > 10000 ORDER BY id LIMIT 10;

3.7 优化COUNT #

sql
-- COUNT(*) 和 COUNT(1) 性能相同
SELECT COUNT(*) FROM users;

-- COUNT(列) 不统计NULL
SELECT COUNT(email) FROM users;

-- 大表计数优化:使用缓存或统计表
SELECT COUNT(*) FROM user_stats WHERE date = CURDATE();

3.8 优化JOIN #

sql
-- 确保JOIN列有索引
CREATE INDEX idx_user_id ON orders(user_id);

-- 小表驱动大表
SELECT * FROM small_table s
JOIN large_table l ON s.id = l.foreign_id;

-- 避免JOIN过多表(建议不超过5个)

四、索引优化 #

4.1 索引设计原则 #

sql
-- 1. 为WHERE条件列创建索引
CREATE INDEX idx_status ON users(status);

-- 2. 为JOIN列创建索引
CREATE INDEX idx_user_id ON orders(user_id);

-- 3. 为ORDER BY列创建索引
CREATE INDEX idx_created ON users(created_at);

-- 4. 组合索引遵循最左前缀
CREATE INDEX idx_status_age ON users(status, age);

-- 有效
SELECT * FROM users WHERE status = 1;
SELECT * FROM users WHERE status = 1 AND age = 25;

-- 无效
SELECT * FROM users WHERE age = 25;

4.2 覆盖索引 #

sql
-- 覆盖索引:查询的列都在索引中
CREATE INDEX idx_name_email ON users(name, email);

-- 使用覆盖索引
SELECT name, email FROM users WHERE name = 'John';

-- Extra显示Using index

4.3 索引选择性 #

sql
-- 计算索引选择性
SELECT 
    COUNT(DISTINCT name) / COUNT(*) AS name_selectivity,
    COUNT(DISTINCT status) / COUNT(*) AS status_selectivity
FROM users;

-- 选择性越高,索引效果越好
-- 选择性接近1的列适合创建索引

4.4 删除冗余索引 #

sql
-- 查看冗余索引
SELECT 
    table_name,
    index_name,
    GROUP_CONCAT(column_name ORDER BY seq_in_index) AS columns
FROM information_schema.statistics
WHERE table_schema = 'mydb'
GROUP BY table_name, index_name;

-- 冗余索引示例
-- INDEX idx_a (a)
-- INDEX idx_ab (a, b)
-- idx_a是冗余的,可以删除

五、配置优化 #

5.1 内存配置 #

ini
[mysqld]
# InnoDB缓冲池(建议设为物理内存的70-80%)
innodb_buffer_pool_size = 4G

# 缓冲池实例数
innodb_buffer_pool_instances = 4

# 日志缓冲区
innodb_log_buffer_size = 16M

# 排序缓冲区
sort_buffer_size = 2M

# 连接缓冲区
join_buffer_size = 2M

# 读缓冲区
read_buffer_size = 1M

# 随机读缓冲区
read_rnd_buffer_size = 1M

5.2 连接配置 #

ini
[mysqld]
# 最大连接数
max_connections = 500

# 最大错误连接数
max_connect_errors = 100

# 连接超时
connect_timeout = 10

# 等待超时
wait_timeout = 28800

# 交互超时
interactive_timeout = 28800

5.3 查询缓存(MySQL 8.0已移除) #

ini
[mysqld]
# MySQL 5.7及以下版本
query_cache_type = 1
query_cache_size = 64M
query_cache_limit = 2M

5.4 慢查询日志 #

ini
[mysqld]
# 开启慢查询日志
slow_query_log = 1

# 慢查询日志文件
slow_query_log_file = /var/log/mysql/slow.log

# 慢查询阈值(秒)
long_query_time = 2

# 记录没有使用索引的查询
log_queries_not_using_indexes = 1

六、监控与分析 #

6.1 慢查询分析 #

sql
-- 查看慢查询配置
SHOW VARIABLES LIKE '%slow_query%';

-- 使用mysqldumpslow分析
-- mysqldumpslow -s t /var/log/mysql/slow.log

-- 使用pt-query-digest分析
-- pt-query-digest /var/log/mysql/slow.log

6.2 性能监控 #

sql
-- 查看状态变量
SHOW GLOBAL STATUS LIKE 'Questions';
SHOW GLOBAL STATUS LIKE 'Queries';
SHOW GLOBAL STATUS LIKE 'Connections';

-- 查看InnoDB状态
SHOW ENGINE INNODB STATUS;

-- 查看缓冲池状态
SHOW STATUS LIKE 'Innodb_buffer_pool%';

-- 查看锁等待
SELECT * FROM sys.innodb_lock_waits;

6.3 性能模式 #

sql
-- 查看性能模式
USE performance_schema;

-- 查看等待事件
SELECT * FROM events_waits_summary_by_instance ORDER BY sum_timer_wait DESC LIMIT 10;

-- 查看语句统计
SELECT * FROM events_statements_summary_by_digest ORDER BY sum_timer_wait DESC LIMIT 10;

-- 查看索引使用情况
SELECT * FROM table_io_waits_summary_by_index_usage WHERE object_schema = 'mydb';

七、总结 #

性能优化要点:

方向 方法
查询优化 使用EXPLAIN分析,避免SELECT *
索引优化 合理创建索引,使用覆盖索引
配置优化 调整内存、连接参数
监控分析 慢查询日志,性能模式

最佳实践:

  1. 先分析后优化
  2. 使用EXPLAIN查看执行计划
  3. 合理使用索引
  4. 定期监控和分析
  5. 优化要循序渐进

恭喜你完成MySQL学习之旅!

最后更新:2026-03-26