一、问题定位与诊断
1. 识别慢查询
sql
复制
下载
-- MySQL 开启慢查询日志
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 2; -- 2秒以上为慢查询
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
-- 查看慢查询统计
SHOW VARIABLES LIKE '%slow_query%';
SHOW STATUS LIKE '%Slow_queries%';
2. 分析执行计划(EXPLAIN)
sql
复制
下载
EXPLAIN SELECT * FROM users WHERE age > 25 AND city = '北京';
EXPLAIN关键字段解读:
| 字段 | 说明 | 优化方向 |
|---|---|---|
| type | 访问类型 | ALL→range→ref→eq_ref→const |
| key | 实际使用的索引 | 确保使用合适索引 |
| rows | 扫描行数 | 尽量减少 |
| Extra | 额外信息 | Using filesort, Using temporary 需要优化 |
二、索引优化方案
1. 创建合适索引
sql
复制
下载
-- 单列索引
CREATE INDEX idx_age ON users(age);
-- 复合索引(最左前缀原则)
CREATE INDEX idx_city_age ON users(city, age);
-- 覆盖索引(索引包含所有查询字段)
CREATE INDEX idx_covering ON users(city, age, name);
-- 前缀索引(文本字段)
CREATE INDEX idx_email_prefix ON users(email(10));
2. 索引优化原则
-
离散度高的列建索引(如性别就不适合)
-
复合索引字段顺序:等值查询→范围查询→排序字段
-
避免冗余索引:定期检查并删除无用索引
-
索引字段尽量小:使用INT而非VARCHAR做主键
3. 索引失效场景
sql
复制
下载
-- 1. 索引列使用函数或计算
SELECT * FROM users WHERE YEAR(create_time) = 2023; -- ❌
SELECT * FROM users WHERE create_time >= '2023-01-01'; -- ✅
-- 2. 隐式类型转换
SELECT * FROM users WHERE phone = 13800138000; -- ❌ phone是varchar
SELECT * FROM users WHERE phone = '13800138000'; -- ✅
-- 3. LIKE以通配符开头
SELECT * FROM users WHERE name LIKE '%张%'; -- ❌
SELECT * FROM users WHERE name LIKE '张%'; -- ✅
-- 4. 使用OR条件(除非所有列都有索引)
SELECT * FROM users WHERE age = 25 OR city = '北京'; -- ❌
三、SQL语句优化
1. 查询语句优化
sql
复制
下载
-- ❌ 避免 SELECT *
SELECT * FROM users WHERE age > 25;
-- ✅ 只取需要的字段
SELECT id, name, age FROM users WHERE age > 25;
-- ❌ 避免在WHERE子句中使用 != 或 <>
SELECT * FROM orders WHERE status != 'completed';
-- ✅ 考虑使用范围查询
SELECT * FROM orders WHERE status IN ('pending', 'processing');
-- ❌ 避免全表扫描的大偏移量分页
SELECT * FROM users LIMIT 1000000, 20;
-- ✅ 使用覆盖索引+子查询优化分页
SELECT * FROM users WHERE id >
(SELECT id FROM users ORDER BY id LIMIT 1000000, 1)
LIMIT 20;
2. JOIN优化
sql
复制
下载
-- ❌ 笛卡尔积
SELECT * FROM users, orders;
-- ✅ 明确JOIN条件
SELECT * FROM users u
JOIN orders o ON u.id = o.user_id;
-- 小表驱动大表
SELECT * FROM small_table s
JOIN large_table l ON s.id = l.small_id;
-- 避免子查询(可转为JOIN)
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount > 100);
-- 改为JOIN
SELECT DISTINCT u.* FROM users u
JOIN orders o ON u.id = o.user_id AND o.amount > 100;
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
3. 批量操作优化
sql
复制
下载
-- ❌ 多次单条插入
INSERT INTO users(name) VALUES ('张三');
INSERT INTO users(name) VALUES ('李四');
-- ✅ 批量插入
INSERT INTO users(name) VALUES ('张三'), ('李四'), ('王五');
-- ❌ 循环更新
UPDATE users SET score = score + 1 WHERE id = 1;
UPDATE users SET score = score + 1 WHERE id = 2;
-- ✅ 批量更新
UPDATE users SET score = score + 1 WHERE id IN (1, 2, 3);
四、数据库设计优化
1. 表结构设计
sql
复制
下载
-- 规范化设计(减少冗余)
-- 但适度反规范化提升查询性能
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
user_id BIGINT,
-- 反规范化:存储用户名,避免JOIN
user_name VARCHAR(50),
amount DECIMAL(10, 2),
INDEX idx_user_id(user_id)
);
-- 垂直拆分大表
-- 将不常用的大字段拆分到扩展表
CREATE TABLE articles (
id BIGINT PRIMARY KEY,
title VARCHAR(200),
summary TEXT,
created_at DATETIME
);
CREATE TABLE article_contents (
article_id BIGINT PRIMARY KEY,
content LONGTEXT, -- 大字段单独存储
FOREIGN KEY (article_id) REFERENCES articles(id)
);
2. 数据类型优化
sql
复制
下载
-- 使用合适的数据类型
TINYINT(1) -- 代替布尔值
INT UNSIGNED -- 非负数
DECIMAL(10, 2) -- 精确小数
VARCHAR(255) -- 避免过大
-- 使用ENUM代替字符串
status ENUM('active', 'inactive', 'pending')
-- 时间戳使用TIMESTAMP(4字节)而非DATETIME(8字节)
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
五、查询缓存与预处理
1. 应用层缓存
java
复制
下载
// Redis缓存查询结果
public User getUserById(Long id) {
String key = "user:" + id;
String cached = redis.get(key);
if (cached != null) {
return JSON.parseObject(cached, User.class);
}
User user = userDao.getById(id);
redis.setex(key, 3600, JSON.toJSONString(user));
return user;
}
2. 数据库查询缓存
sql
复制
下载
-- MySQL查询缓存(MySQL 8.0已移除)
-- 使用应用层缓存替代
-- 使用预处理语句
PREPARE stmt FROM 'SELECT * FROM users WHERE age > ? AND city = ?';
SET @age = 25, @city = '北京';
EXECUTE stmt USING @age, @city;
DEALLOCATE PREPARE stmt;
六、分库分表策略
1. 水平分表
sql
复制
下载
-- 按用户ID取模分表
CREATE TABLE users_0 LIKE users_template;
CREATE TABLE users_1 LIKE users_template;
-- ...创建users_0到users_9
-- 路由规则
table_suffix = user_id % 10;
sql = "SELECT * FROM users_" + table_suffix + " WHERE id = ?";
2. 分库分表中间件
yaml
复制
下载
# ShardingSphere配置示例
spring:
shardingsphere:
datasource:
names: ds0, ds1
sharding:
tables:
orders:
actual-data-nodes: ds$->{0..1}.orders_$->{0..15}
table-strategy:
inline:
sharding-column: order_id
algorithm-expression: orders_$->{order_id % 16}
database-strategy:
inline:
sharding-column: user_id
algorithm-expression: ds$->{user_id % 2}
七、读写分离与负载均衡
1. 主从架构
sql
复制
下载
-- 主库写操作
INSERT INTO orders (...) VALUES (...);
-- 从库读操作
SELECT * FROM orders WHERE ...; -- 读请求路由到从库
-- 同步延迟处理方案
-- 1. 写后立即读,强制走主库
-- 2. 关键业务读主库
-- 3. 容忍秒级延迟
2. 连接池配置
java
复制
下载
// HikariCP配置
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://master:3306/db");
config.setMaximumPoolSize(20);
config.setMinimumIdle(10);
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
config.setMaxLifetime(1800000);
// 读写分离数据源
@Bean
@Primary
public DataSource dataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource());
targetDataSources.put("slave", slaveDataSource());
AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() {
@Override
protected Object determineCurrentLookupKey() {
return TransactionSynchronizationManager.isCurrentTransactionReadOnly()
? "slave" : "master";
}
};
routingDataSource.setTargetDataSources(targetDataSources);
return routingDataSource;
}
八、监控与调优工具
1. 监控工具集
bash
复制
下载
# 1. 实时监控
mysqladmin -u root -p extended-status | grep -i "slow"
# 2. 分析慢查询日志
mysqldumpslow -s t /var/log/mysql/slow.log
# 3. Percona Toolkit分析
pt-query-digest /var/log/mysql/slow.log
# 4. 实时诊断
SHOW PROCESSLIST; -- 查看当前连接
SHOW ENGINE INNODB STATUS; -- InnoDB状态
2. 性能分析流程
sql
复制
下载
-- 1. 找出慢查询
SELECT * FROM mysql.slow_log
WHERE query_time > 5
ORDER BY query_time DESC
LIMIT 10;
-- 2. 分析执行计划
EXPLAIN FORMAT=JSON SELECT ...;
-- 3. 优化建议
SELECT * FROM sys.schema_unused_indexes; -- 未使用索引
SELECT * FROM sys.statements_with_full_table_scans; -- 全表扫描
九、实战案例
案例1:电商订单查询优化
sql
复制
下载
-- 原始慢查询(执行时间:8.2s)
SELECT * FROM orders
WHERE user_id = 123
AND status IN (1, 2, 3)
AND create_time > '2023-01-01'
ORDER BY create_time DESC
LIMIT 0, 20;
-- 优化步骤:
-- 1. 创建复合索引
CREATE INDEX idx_user_status_time ON orders(user_id, status, create_time);
-- 2. 优化SQL(使用覆盖索引)
SELECT id, order_no, amount, status, create_time
FROM orders
WHERE user_id = 123
AND status IN (1, 2, 3)
AND create_time > '2023-01-01'
ORDER BY create_time DESC
LIMIT 0, 20;
-- 3. 结果:执行时间降至0.02s
案例2:大数据量表的分页优化
sql
复制
下载
-- 原始:LIMIT 1000000, 20(很慢)
SELECT * FROM user_actions ORDER BY id LIMIT 1000000, 20;
-- 优化1:使用覆盖索引
SELECT * FROM user_actions
WHERE id >= (SELECT id FROM user_actions ORDER BY id LIMIT 1000000, 1)
ORDER BY id LIMIT 20;
-- 优化2:记录上次查询的最大ID
-- 第一页
SELECT * FROM user_actions ORDER BY id LIMIT 20; -- 记下最后一条id=20
-- 第二页
SELECT * FROM user_actions WHERE id > 20 ORDER BY id LIMIT 20;
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
十、预防措施与最佳实践
1. 开发规范
sql
复制
下载
-- SQL编写规范
-- 1. 禁止SELECT *
-- 2. UPDATE/DELETE必须有WHERE条件
-- 3. 大批量操作分批进行
-- 4. 重要查询必须review执行计划
-- 5. 定期清理无用索引和历史数据
2. 定期维护
sql
复制
下载
-- 每周执行
OPTIMIZE TABLE big_table; -- 整理碎片
ANALYZE TABLE important_table; -- 更新统计信息
-- 每天监控
-- 1. 慢查询数量增长
-- 2. 索引使用情况
-- 3. 锁等待时间
-- 4. 连接数监控
3. 应急预案
sql
复制
下载
-- 当出现性能问题时:
-- 1. 立即kill慢查询
SHOW PROCESSLIST;
KILL [query_id];
-- 2. 临时增加索引
CREATE INDEX idx_temp ON problem_table(problem_column);
-- 3. 修改SQL_HINT强制使用索引
SELECT /*+ INDEX(table_name index_name) */ * FROM table_name;
-- 4. 降级方案:返回部分数据或缓存数据
总结:慢查询优化检查清单
-
✅ 是否使用EXPLAIN分析执行计划?
-
✅ 是否创建了合适的索引?
-
✅ 是否避免了索引失效场景?
-
✅ 是否使用覆盖索引?
-
✅ 是否优化了JOIN查询?
-
✅ 是否优化了分页查询?
-
✅ **是否避免了SELECT ***?
-
✅ 是否使用批量操作?
-
✅ 表结构设计是否合理?
-
✅ 是否使用了读写分离?
-
✅ 是否有缓存策略?
-
✅ 是否有监控和告警?
优化顺序建议:
-
优先优化:索引设计、SQL语句
-
其次优化:数据库参数、硬件升级
-
最后考虑:架构改造(分库分表、读写分离)
记住:80%的性能问题可以通过优化索引和SQL解决,剩下的20%才需要考虑架构调整。