1. MySQL慢查询优化实战指南
1.1 慢查询的本质与定位方法
慢查询的本质是数据库执行SQL语句时消耗了过多资源,导致响应时间超出预期。在实际生产环境中,我们通常将执行时间超过500ms的查询定义为慢查询(这个阈值可根据业务需求调整)。
定位慢查询的核心工具组合:
- 慢查询日志:MySQL内置的慢查询记录功能
sql复制# 启用慢查询日志(建议在测试环境先验证)
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 0.5; # 设置慢查询阈值为500ms
SET GLOBAL slow_query_log_file = '/var/log/mysql/mysql-slow.log';
- EXPLAIN命令:分析SQL执行计划的金钥匙
sql复制EXPLAIN SELECT * FROM orders WHERE user_id = 10086;
关键执行计划指标解读:
- type列:从最优到最差依次为 system > const > eq_ref > ref > range > index > ALL
- key列:实际使用的索引
- rows列:预估需要扫描的行数
- Extra列:重要补充信息(Using filesort、Using temporary等)
1.2 SQL与索引优化实战
1.2.1 索引设计黄金法则
- 最左前缀原则:联合索引(a,b,c)只能支持a、ab、abc三种查询条件组合
- 覆盖索引优先:SELECT的字段尽量都包含在索引中
- 基数选择性:区分度高的列更适合建索引(如手机号比性别更适合)
sql复制# 不良索引示例
CREATE INDEX idx_status ON orders(status); # status只有几种取值,区分度低
# 优化后的联合索引
CREATE INDEX idx_user_product ON orders(user_id, product_id);
1.2.2 SQL编写避坑指南
常见性能陷阱及解决方案:
| 问题类型 | 错误示例 | 优化方案 |
|---|---|---|
| 隐式类型转换 | WHERE user_id = '10086' (user_id是int) |
保持类型一致 |
| 函数操作索引列 | WHERE DATE(create_time) = '2023-01-01' |
WHERE create_time BETWEEN '2023-01-01 00:00:00' AND '2023-01-01 23:59:59' |
| 深分页 | LIMIT 100000, 20 |
使用覆盖索引+延迟关联 |
1.3 表结构优化进阶技巧
1.3.1 字段类型优化
- 金额字段:用DECIMAL(19,4)而不是DOUBLE
- 状态字段:TINYINT比VARCHAR(10)更高效
- 大文本:TEXT类型单独拆表存储
1.3.2 数据归档策略
对于历史数据,可采用时间维度归档:
sql复制# 创建归档表(结构与原表相同)
CREATE TABLE orders_archive LIKE orders;
# 迁移历史数据
INSERT INTO orders_archive
SELECT * FROM orders WHERE create_time < '2022-01-01';
# 删除原表过期数据
DELETE FROM orders WHERE create_time < '2022-01-01';
1.4 架构级优化方案
1.4.1 读写分离实现
典型架构:
code复制主库(写) -> 从库1(读)
-> 从库2(读)
配置要点:
sql复制# 主库配置
[mysqld]
server-id = 1
log_bin = mysql-bin
binlog_format = ROW
# 从库配置
[mysqld]
server-id = 2
relay_log = mysql-relay-bin
read_only = ON
1.4.2 缓存层设计
多级缓存策略:
- 客户端缓存(HTTP缓存头)
- 应用层缓存(Redis/Memcached)
- 数据库缓存(InnoDB Buffer Pool)
缓存更新策略对比:
- Cache Aside:先更新DB再删除缓存
- Write Through:同时更新缓存和DB
- Write Behind:先更新缓存,异步批量写DB
2. 分库分表深度解析
2.1 分库分表核心原理
2.1.1 拆分维度选择
垂直拆分 vs 水平拆分:
| 拆分类型 | 特点 | 适用场景 |
|---|---|---|
| 垂直分库 | 按业务拆分 | 业务模块清晰 |
| 垂直分表 | 按字段拆分 | 表字段多且冷热分明 |
| 水平分库 | 同业务多库 | 高并发场景 |
| 水平分表 | 同表多数据分片 | 大数据量表 |
2.1.2 分片键选择策略
优秀分片键的特征:
- 查询频率高(80%查询能命中分片)
- 数据分布均匀(避免热点)
- 值稳定(不经常修改)
常见分片方案对比:
| 方案 | 优点 | 缺点 |
|---|---|---|
| 取模 | 简单均匀 | 扩容困难 |
| 范围 | 易于扩容 | 可能热点 |
| 哈希 | 分布均匀 | 无法范围查询 |
| 时间 | 冷热分离 | 近期数据热点 |
2.2 分库分表实战方案
2.2.1 ShardingSphere配置示例
yaml复制# 数据源配置
spring:
shardingsphere:
datasource:
names: ds0,ds1
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db0
username: root
password: 123456
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db1
username: root
password: 123456
sharding:
tables:
t_order:
actual-data-nodes: ds$->{0..1}.t_order_$->{0..15}
table-strategy:
inline:
sharding-column: order_id
algorithm-expression: t_order_$->{order_id % 16}
database-strategy:
inline:
sharding-column: user_id
algorithm-expression: ds$->{user_id % 2}
2.2.2 分布式ID生成方案
雪花算法实现要点:
java复制public class SnowflakeIdGenerator {
private final long twepoch = 1288834974657L;
private final long workerIdBits = 5L;
private final long datacenterIdBits = 5L;
private final long sequenceBits = 12L;
private final long workerIdShift = sequenceBits;
private final long datacenterIdShift = sequenceBits + workerIdBits;
private final long timestampShift = sequenceBits + workerIdBits + datacenterIdBits;
public synchronized long nextId() {
long timestamp = timeGen();
// 实现细节省略
}
}
2.3 分库分表后的问题解决方案
2.3.1 跨分片查询处理
- 分页查询:使用归并排序
sql复制# 逻辑分页(不推荐)
SELECT * FROM t_order ORDER BY create_time DESC LIMIT 10000, 20;
# 优化方案:使用上次查询的最大ID
SELECT * FROM t_order WHERE id > 10000 ORDER BY id LIMIT 20;
- 聚合计算:使用并行查询+内存计算
java复制// 伪代码示例
List<Future<Result>> futures = shards.parallelStream()
.map(shard -> executor.submit(() -> queryShard(shard)))
.collect(Collectors.toList());
Result finalResult = futures.stream()
.map(Future::get)
.reduce(Result::merge)
.get();
2.3.2 分布式事务方案
Seata AT模式工作流程:
- TM向TC发起全局事务
- RM注册分支事务
- RM报告事务状态
- TC驱动全局提交/回滚
2.4 分库分表监控与运维
2.4.1 关键监控指标
- 分片均衡度:各分片数据量差异
- 热点检测:分片QPS监控
- 慢查询分析:分片后SQL性能
2.4.2 扩容迁移方案
双写迁移流程:
- 新老库同时写入
- 数据校验工具比对差异
- 差异数据同步
- 流量切换验证
- 下线老库
3. 实战经验与避坑指南
3.1 慢查询优化常见误区
- 过度索引:索引不是越多越好,每个索引都会增加写入开销
- 盲目使用JOIN:大表JOIN容易成为性能瓶颈
- 忽视连接池配置:不合理的连接池参数会导致连接风暴
3.2 分库分表实施心得
- 循序渐进原则:先读写分离,再分表,最后分库
- 保留单测能力:开发环境保留单库单表测试模式
- 监控先行:在拆分前建立完善的监控体系
3.3 性能优化检查清单
- [ ] EXPLAIN分析执行计划
- [ ] 确认索引使用情况
- [ ] 检查锁等待情况
- [ ] 评估连接池利用率
- [ ] 监控磁盘IO和CPU负载
在实际项目中,我们曾遇到一个典型案例:用户订单查询在数据量达到3000万时明显变慢。通过分析发现主要问题是订单状态字段没有合理索引,且存在深分页查询。解决方案是:
- 添加(user_id, status)的联合索引
- 改造分页为"上一页/下一页"模式
- 将3个月前的订单归档到历史表
优化后查询响应时间从1200ms降至80ms