MySQL 数据库性能优化:从 10 秒到 100 毫秒的实战指南
在生产环境中,数据库性能问题往往是系统瓶颈的根源。本文将分享实际的 MySQL 优化经验,帮助你将查询时间从秒级降低到毫秒级。
一、诊断性能问题
在优化之前,首先需要找到性能瓶颈所在。
1.1 启用慢查询日志
-- 查看慢查询日志状态
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';
-- 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1; -- 记录超过 1 秒的查询
SET GLOBAL log_queries_not_using_indexes = 'ON';
1.2 分析慢查询
# 使用 mysqldumpslow 分析慢查询日志
mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log
# 或使用 pt-query-digest(Percona Toolkit)
pt-query-digest /var/log/mysql/mysql-slow.log
二、索引优化策略
2.1 创建合适的索引
-- 查看表索引
SHOW INDEX FROM users;
-- 为常用查询字段创建索引
CREATE INDEX idx_email ON users(email);
CREATE INDEX idx_created_at ON orders(created_at);
-- 创建复合索引(注意字段顺序)
CREATE INDEX idx_user_status_created ON orders(user_id, status, created_at);
2.2 索引最佳实践
- 最左前缀原则:复合索引 (a,b,c) 可以支持 a、(a,b)、(a,b,c) 的查询
- 选择性高的字段优先:性别字段不适合单独建索引
- 避免过度索引:每个索引都会增加写入开销
- 覆盖索引:查询字段都在索引中可避免回表
2.3 使用 EXPLAIN 分析查询
EXPLAIN SELECT * FROM orders
WHERE user_id = 123
AND status = 'pending'
ORDER BY created_at DESC
LIMIT 10;
-- 关注以下字段:
-- type: 访问类型(system > const > eq_ref > ref > range > index > ALL)
-- key: 实际使用的索引
-- rows: 预估扫描行数
-- Extra: 额外信息(Using index 表示覆盖索引,Using filesort 需要优化)
三、查询优化技巧
3.1 避免 SELECT *
-- ❌ 不推荐
SELECT * FROM users WHERE id = 1;
-- ✅ 推荐:只查询需要的字段
SELECT id, name, email FROM users WHERE id = 1;
3.2 优化分页查询
-- ❌ 深度分页性能差
SELECT * FROM orders ORDER BY created_at DESC LIMIT 100000, 20;
-- ✅ 使用游标分页
SELECT * FROM orders
WHERE created_at < '2026-01-01 00:00:00'
ORDER BY created_at DESC
LIMIT 20;
-- ✅ 或使用子查询优化
SELECT o.* FROM orders o
INNER JOIN (
SELECT id FROM orders ORDER BY created_at DESC LIMIT 100000, 20
) tmp ON o.id = tmp.id;
3.3 优化 JOIN 操作
-- ✅ 确保 JOIN 字段有索引
-- ✅ 小表驱动大表
-- ✅ 避免多表 JOIN(超过 3 表考虑应用层组装)
-- 优化前
SELECT * FROM orders o
LEFT JOIN users u ON o.user_id = u.id
LEFT JOIN products p ON o.product_id = p.id
WHERE o.created_at > '2026-01-01';
-- 优化后:拆分查询,应用层组装
SELECT id, user_id, product_id FROM orders WHERE created_at > '2026-01-01';
SELECT id, name FROM users WHERE id IN (...);
SELECT id, name FROM products WHERE id IN (...);
四、表结构优化
4.1 选择合适的数据类型
-- ❌ 浪费空间
CREATE TABLE users (
id INT, -- 如果 ID 不会超过 65535,用 SMALLINT
phone VARCHAR(255), -- 手机号用 VARCHAR(11) 或 CHAR(11)
status TINYINT -- 状态用 TINYINT 而非 INT
);
-- ✅ 优化后
CREATE TABLE users (
id SMALLINT UNSIGNED,
phone CHAR(11),
status TINYINT
);
4.2 垂直分表
-- 将大字段分离到扩展表
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50),
email VARCHAR(100),
created_at TIMESTAMP
);
CREATE TABLE users_profile (
user_id INT PRIMARY KEY,
bio TEXT,
avatar VARCHAR(255),
settings JSON
);
五、配置优化
5.1 关键配置参数
# my.cnf 优化配置
[mysqld]
# 内存分配
innodb_buffer_pool_size = 4G # 物理内存的 50-70%
innodb_log_file_size = 512M
innodb_log_buffer_size = 64M
# 连接数
max_connections = 500
thread_cache_size = 100
# 查询缓存(MySQL 5.7 及之前)
query_cache_size = 64M
query_cache_type = 1
# InnoDB 设置
innodb_flush_log_at_trx_commit = 2 # 1=最安全,2=性能更好
innodb_flush_method = O_DIRECT
innodb_io_capacity = 2000
# 日志
slow_query_log = 1
long_query_time = 1
log_queries_not_using_indexes = 1
六、实战案例
案例:订单查询从 10 秒优化到 100 毫秒
原始查询:
SELECT * FROM orders
WHERE user_id = 12345
AND created_at > '2026-01-01'
ORDER BY created_at DESC;
-- 执行时间:10.5 秒,扫描 500 万行
优化步骤:
- 创建复合索引:
CREATE INDEX idx_user_created ON orders(user_id, created_at); - 只查询必要字段
- 添加 LIMIT 限制
优化后:
SELECT id, order_no, amount, status, created_at
FROM orders
WHERE user_id = 12345
AND created_at > '2026-01-01'
ORDER BY created_at DESC
LIMIT 20;
-- 执行时间:85 毫秒,扫描 20 行
七、监控与维护
7.1 实时监控
-- 查看当前连接
SHOW PROCESSLIST;
-- 查看表锁
SHOW OPEN TABLES WHERE In_use > 0;
-- 查看 InnoDB 状态
SHOW ENGINE INNODB STATUS;
-- 查看性能指标
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Innodb_buffer_pool_reads';
7.2 定期维护
-- 分析表(更新统计信息)
ANALYZE TABLE orders;
-- 优化表(整理碎片)
OPTIMIZE TABLE orders;
-- 检查表完整性
CHECK TABLE orders;
总结
MySQL 性能优化是一个系统工程,需要:
- ✅ 建立监控体系,及时发现慢查询
- ✅ 合理使用索引,避免全表扫描
- ✅ 优化 SQL 语句,减少不必要的数据读取
- ✅ 根据业务特点调整表结构
- ✅ 配置合适的服务器参数
- ✅ 定期维护,保持数据库健康
记住:优化不是一次性的工作,而是需要持续关注和调整的过程。建立完善的监控和告警机制,让性能问题在影响用户之前就被发现和解决。
文章评论