MySQL的性能优化
wabicai
# MySQL的性能优化
# 概述
MySQL性能优化是数据库管理中的重要环节,直接影响系统的响应速度和并发处理能力。本文将从索引优化、SQL语句优化、表结构设计和配置参数优化等方面进行总结。
# 索引优化
# 1. 选择合适的索引类型
- B+Tree索引:最常用的索引类型,适合范围查询和排序
- Hash索引:适合等值查询,不支持范围查询
- 全文索引:用于全文检索
- 空间索引:用于地理数据
# 2. 索引设计原则
-- 为经常查询的字段添加索引
CREATE INDEX idx_user_email ON users(email);
-- 联合索引遵循最左前缀原则
CREATE INDEX idx_user_name_age ON users(name, age);
-- 避免在小基数字段上建立索引(如性别)
-- 避免在频繁更新的字段上建立过多索引
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 3. 索引失效场景
- 在索引列上使用函数或表达式
- 使用
!=或<>操作符 - 使用
OR连接条件(可改用UNION) - 字符串不加引号导致隐式类型转换
LIKE以%开头
# SQL语句优化
# 1. 避免SELECT *
-- 不推荐
SELECT * FROM users WHERE id = 1;
-- 推荐:只查询需要的字段
SELECT id, name, email FROM users WHERE id = 1;
1
2
3
4
5
2
3
4
5
# 2. 使用LIMIT限制返回行数
SELECT id, name FROM users LIMIT 100;
1
# 3. 避免N+1查询问题
-- 不推荐:N+1查询
SELECT * FROM orders;
-- 然后循环查询每个订单的用户信息
SELECT * FROM users WHERE id = ?;
-- 推荐:使用JOIN一次查询
SELECT o.*, u.name
FROM orders o
LEFT JOIN users u ON o.user_id = u.id;
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 4. 使用EXISTS代替IN
-- 对于大数据量,EXISTS通常比IN更快
SELECT * FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o WHERE o.user_id = u.id
);
1
2
3
4
5
2
3
4
5
# 5. 批量操作代替循环插入
-- 不推荐:循环单条插入
INSERT INTO users VALUES (1, 'Alice');
INSERT INTO users VALUES (2, 'Bob');
-- 推荐:批量插入
INSERT INTO users VALUES
(1, 'Alice'),
(2, 'Bob'),
(3, 'Charlie');
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 表结构设计优化
# 1. 选择合适的数据类型
- 尽量使用数字类型代替字符串类型
- VARCHAR比CHAR更节省空间(对于可变长度字符串)
- 使用TIMESTAMP代替DATETIME(4字节 vs 8字节)
- 整数类型根据范围选择(TINYINT, SMALLINT, INT, BIGINT)
# 2. 表拆分
# 垂直拆分
将常用字段和不常用字段分开存储:
-- 用户基本信息表
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50),
email VARCHAR(100)
);
-- 用户详细信息表
CREATE TABLE user_profiles (
user_id INT PRIMARY KEY,
bio TEXT,
avatar_url VARCHAR(255),
FOREIGN KEY (user_id) REFERENCES users(id)
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 水平拆分(分表)
按照某个维度将数据分散到多个表:
-- 按年份分表
CREATE TABLE orders_2023 (...);
CREATE TABLE orders_2024 (...);
1
2
3
2
3
# 3. 冗余字段设计
适当冗余可以减少JOIN操作:
-- 在订单表中冗余用户名称
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT,
user_name VARCHAR(50), -- 冗余字段
amount DECIMAL(10,2)
);
1
2
3
4
5
6
7
2
3
4
5
6
7
# 配置参数优化
# 1. InnoDB缓冲池大小
# my.cnf
[mysqld]
# 通常设置为物理内存的50%-80%
innodb_buffer_pool_size = 4G
1
2
3
4
2
3
4
# 2. 连接数配置
# 最大连接数
max_connections = 500
# 线程缓存大小
thread_cache_size = 50
1
2
3
4
5
2
3
4
5
# 3. 查询缓存(MySQL 5.7及以下)
query_cache_type = 1
query_cache_size = 128M
1
2
2
注意:MySQL 8.0已移除查询缓存功能。
# 4. 慢查询日志
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2 # 记录执行超过2秒的查询
1
2
3
2
3
# 查询分析工具
# 1. EXPLAIN分析执行计划
EXPLAIN SELECT * FROM users WHERE age > 20;
1
关注字段:
type:访问类型(ALL, index, range, ref, eq_ref, const)key:实际使用的索引rows:扫描的行数Extra:额外信息(Using filesort, Using temporary需要优化)
# 2. SHOW PROFILE
SET profiling = 1;
SELECT * FROM users WHERE age > 20;
SHOW PROFILES;
SHOW PROFILE FOR QUERY 1;
1
2
3
4
2
3
4
# 硬件和架构优化
# 1. 主从复制
- 读写分离:主库写,从库读
- 提高并发能力和可用性
# 2. 分库分表
- 使用ShardingSphere等中间件
- 水平拆分(按用户ID、时间等维度)
# 3. 使用缓存
- Redis缓存热点数据
- 减轻数据库压力
# 最佳实践总结
- 定期分析慢查询日志,优化慢SQL
- 合理设计索引,避免过度索引
- 避免大事务,拆分为小事务
- 定期执行OPTIMIZE TABLE,整理表碎片
- 监控数据库性能指标(QPS、TPS、连接数等)
- 定期备份数据,防止数据丢失
- 使用连接池,避免频繁创建连接
- 分页查询优化:使用延迟关联或书签方式
-- 延迟关联分页优化
SELECT * FROM users u
INNER JOIN (
SELECT id FROM users LIMIT 10000, 20
) AS t ON u.id = t.id;
1
2
3
4
5
2
3
4
5
# 参考资料
- 《高性能MySQL》
- MySQL官方文档优化指南
- 阿里巴巴《Java开发手册》中的MySQL规约