中间件性能调优完全指南(补充:MySQL 调优篇)
本文最后更新于20 天前,其中的信息可能已经过时,如有错误请发送邮件到2219571407@qq.com

本文是《中间件性能调优完全指南》的补充章节,系统梳理 MySQL 数据库的性能调优方法,涵盖 SQL 优化、索引设计、参数调优、架构演进等维度。


一、MySQL 调优概述

1.1 调优金字塔

graph TB
    A[SQL与索引优化<br>性价比最高] --> B[参数配置调优<br>数据库层面]
    B --> C[架构优化<br>读写分离/分库分表]
    C --> D[硬件升级<br>SSD/内存扩容]

核心原则:从上到下,性价比递减。优先优化 SQL 和索引,最后才考虑加钱升级硬件。

1.2 调优目标

指标说明健康标准
查询响应时间SQL 执行耗时OLTP < 100ms,OLAP < 5s
QPS/TPS每秒查询/事务数根据业务评估
慢查询比例慢查询占总查询比例< 1%
连接数使用率当前连接数/最大连接数< 70%
InnoDB 缓冲池命中率数据从内存读取的比例> 99%

二、SQL 语句优化

2.1 执行计划分析(EXPLAIN)

-- 查看执行计划
EXPLAIN SELECT * FROM orders WHERE user_id = 123;

-- 关键指标解读
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
type(越往下越差)含义优化方向
system/const最多一条匹配最优
eq_ref唯一索引匹配很好
ref非唯一索引匹配可接受
range范围扫描需关注
index全索引扫描需优化
ALL全表扫描🔴 必须优化

2.2 常见 SQL 反模式与优化

反模式错误写法正确写法提升
**SELECT ***SELECT * FROM usersSELECT id, name FROM users减少 I/O 50%+
无分页一次查全部LIMIT 100 OFFSET 0避免 OOM
函数在条件列WHERE DATE(create_time) = '2024-01-01'WHERE create_time >= '2024-01-01' AND create_time < '2024-01-02'索引生效
隐式类型转换WHERE phone = 13800138000(phone 是 varchar)WHERE phone = '13800138000'索引生效
前导通配符WHERE name LIKE '%张三%'考虑使用全文索引或 ES避免全表扫描
OR 乱用WHERE id = 1 OR id = 2 OR id = 3WHERE id IN (1,2,3)优化器更好优化
NOT IN 子查询WHERE id NOT IN (SELECT user_id FROM orders)LEFT JOIN ... WHERE orders.user_id IS NULL性能提升 10 倍+

2.3 批量操作优化

-- ❌ 不推荐:逐条插入(1000 次网络往返)
INSERT INTO users (name) VALUES ('a');
INSERT INTO users (name) VALUES ('b');
-- ...

-- ✅ 推荐:批量插入(1 次网络往返)
INSERT INTO users (name) VALUES ('a'), ('b'), ('c'), ...;

-- ❌ 不推荐:逐条更新
UPDATE users SET status = 1 WHERE id = 1;
UPDATE users SET status = 1 WHERE id = 2;

-- ✅ 推荐:IN 条件批量更新
UPDATE users SET status = 1 WHERE id IN (1,2,3,4,5);

三、索引设计与优化

3.1 索引类型与选择

索引类型适用场景注意事项
B-Tree(默认)等值查询、范围查询、排序最通用
Hash(Memory引擎)精确等值查询不支持范围查询
全文索引全文搜索(LIKE ‘%xxx%’)中文需要分词插件
联合索引多条件查询遵循最左前缀原则
覆盖索引查询字段都在索引中避免回表,性能翻倍
唯一索引需要唯一性约束兼具查询加速
降序索引(8.0)ORDER BY 降序避免 filesort

3.2 索引设计黄金法则

法则说明示例
最左前缀联合索引按最左列匹配INDEX(a,b,c) 可匹配 (a)(a,b)(a,b,c)
区分度优先高区分度列放前面手机号 > 性别
索引覆盖查询字段尽量都在索引中SELECT id,name FROM users WHERE phone='xxx'
避免重复索引相同前缀的索引冗余INDEX(a)INDEX(a,b) 保留后者即可
索引下推MySQL 5.6+ 自动优化减少回表次数

3.3 索引失效场景

-- 🔴 索引失效:使用函数
WHERE LEFT(phone, 3) = '138'  -- 失效
-- ✅ 改为
WHERE phone LIKE '138%'       -- 有效

-- 🔴 索引失效:隐式类型转换
WHERE phone = 13800138000     -- phone 是 varchar,失效
-- ✅ 改为
WHERE phone = '13800138000'   -- 有效

-- 🔴 索引失效:OR 连接非索引字段
WHERE id = 1 OR name = '张三' -- name 无索引,失效
-- ✅ 改为两次查询或用 UNION

-- 🔴 索引失效:NOT IN / <>
WHERE status <> 0             -- 失效(数据量小时可能走索引)

3.4 索引监控与维护

-- 查看表索引
SHOW INDEX FROM users;

-- 查看索引使用情况(MySQL 5.6+)
SELECT * FROM sys.schema_unused_indexes;

-- 查看冗余索引
SELECT * FROM sys.schema_redundant_indexes;

-- 重建索引(回收空间)
ALTER TABLE users ENGINE=InnoDB;

-- 删除未使用的索引
DROP INDEX unused_idx ON users;

四、数据库参数调优

4.1 核心参数全景

参数分类参数名默认值推荐值说明
内存类innodb_buffer_pool_size128MB物理内存 50-70%InnoDB 缓冲池
innodb_log_buffer_size16MB64-256MB日志缓冲区
key_buffer_size8MB64MB(MyISAM 用)MyISAM 索引缓冲
query_cache_size1MB0(8.0 已移除)查询缓存(不推荐)
日志类innodb_log_file_size48MB1-4GBRedo 日志文件大小
innodb_flush_log_at_trx_commit11/2刷盘频率
sync_binlog11/100Binlog 同步频率
连接类max_connections151500-2000最大连接数
innodb_thread_concurrency064-128并发线程数
IO类innodb_io_capacity2002000-20000IO 吞吐能力
innodb_flush_neighbors10(SSD)刷盘邻居页
其他tmp_table_size16MB64-256MB内存临时表阈值

4.2 InnoDB 缓冲池调优(最重要)

-- 查看当前缓冲池大小
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
-- 设置为物理内存的 50-70%(以 16GB 内存为例)
SET GLOBAL innodb_buffer_pool_size = 10 * 1024 * 1024 * 1024;  -- 10GB

-- 查看缓冲池命中率(应 > 99%)
SHOW ENGINE INNODB STATUS\G
-- 关注 Buffer pool hit rate: 1000/1000 (100%)

-- MySQL 5.7+ 可在线调整
SET GLOBAL innodb_buffer_pool_size = 12 * 1024 * 1024 * 1024;

-- 缓冲池预热(重启后)
SET GLOBAL innodb_buffer_pool_dump_at_shutdown = ON;
SET GLOBAL innodb_buffer_pool_load_at_startup = ON;

4.3 日志刷盘策略(性能 vs 可靠权衡)

配置组合性能可靠性适用场景
innodb_flush_log_at_trx_commit=1 + sync_binlog=1最高(不丢数据)金融、支付
innodb_flush_log_at_trx_commit=1 + sync_binlog=100高(丢最多100个事务)电商、订单
innodb_flush_log_at_trx_commit=2 + sync_binlog=100日志、监控
innodb_flush_log_at_trx_commit=2 + sync_binlog=0最高低(丢1秒数据)数据仓库

4.4 连接池配置(应用层配合)

# 以 HikariCP 为例
spring.datasource.hikari.maximumPoolSize=50
spring.datasource.hikari.minimumIdle=10
spring.datasource.hikari.idleTimeout=300000
spring.datasource.hikari.connectionTimeout=30000
spring.datasource.hikari.leakDetectionThreshold=60000

计算公式

连接池大小 ≈ (CPU核心数 × 2) + 有效磁盘并发数
实际建议:50-200(取决于数据库规格)

五、慢查询治理

5.1 开启慢查询日志

-- 开启慢查询日志
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;          -- 超过 1 秒记录
SET GLOBAL log_queries_not_using_indexes = ON;  -- 记录无索引查询
SET GLOBAL log_slow_admin_statements = ON;      -- 记录慢 DDL

-- 查看慢查询日志位置
SHOW VARIABLES LIKE 'slow_query_log_file';

5.2 pt-query-digest 分析慢日志

# 安装 percona-toolkit
apt-get install percona-toolkit

# 分析慢查询日志
pt-query-digest /var/log/mysql/slow.log > slow_report.txt

# 输出示例
# 排名        响应时间    调用次数    查询样例
# ==============================================
# 1           45.23s     124        SELECT * FROM orders WHERE user_id = ?
# 2           23.11s     89         UPDATE users SET last_login = NOW()

5.3 慢查询治理流程

graph LR
    A[监控发现慢查询] --> B[EXPLAIN 分析执行计划]
    B --> C{是否用到索引?}
    C -->|否| D[添加/优化索引]
    C -->|是| E{SQL 是否可优化?}
    E -->|是| F[重写 SQL]
    E -->|否| G[考虑架构优化]
    D --> H[压测验证]
    F --> H
    G --> H
    H --> I[上线]

六、架构层面优化

6.1 读写分离

graph LR
    A[业务应用] --> B[读写分离中间件]
    B -->|写| C[主库]
    B -->|读| D[从库1]
    B -->|读| E[从库2]

适用场景:读多写少(读写比 > 5:1)

注意事项

  • 主从延迟问题(业务容忍度决定是否读主库)
  • 从库故障自动摘除

6.2 分库分表

策略说明示例
水平分表同库,表拆分user_0, user_1, user_2
水平分库不同库db0.user, db1.user
垂直分表列拆分user_base, user_extend
垂直分库不同业务独立订单库、用户库

分片键选择原则

  • 高频查询字段(如 user_id)
  • 分布均匀(避免热点)
  • 不可变(一旦确定不再更改)

6.3 缓存策略

层级缓存方案适用场景
应用本地缓存Caffeine、Guava字典类、配置类
分布式缓存Redis、Memcached用户会话、热点数据
数据库查询缓存MySQL Query Cache(8.0 移除)不推荐使用

6.4 冷热分离

-- 热数据(近期订单)
CREATE TABLE orders_hot LIKE orders;

-- 冷数据(历史订单)
CREATE TABLE orders_cold LIKE orders;

-- 定期归档
INSERT INTO orders_cold SELECT * FROM orders WHERE create_time < '2024-01-01';
DELETE FROM orders WHERE create_time < '2024-01-01';

七、MySQL 8.0 新特性与调优

7.1 新特性带来的调优机会

特性调优价值
不可见索引安全删除索引:先设为不可见,观察无影响再删除
降序索引优化 ORDER BY DESC 查询,避免 filesort
隐藏索引测试新索引效果,不影响生产
资源组限制查询使用 CPU 核数
即时 DDL添加列不复制表,操作秒级完成
EXPLAIN ANALYZE获取真实执行计划耗时

7.2 实战示例

-- 1. 不可见索引(安全删除)
ALTER TABLE users ALTER INDEX idx_name INVISIBLE;
-- 观察一段时间,确认无影响后删除
ALTER TABLE users DROP INDEX idx_name;

-- 2. 降序索引(8.0 原生支持)
CREATE INDEX idx_time_desc ON orders (create_time DESC);

-- 3. EXPLAIN ANALYZE(获取真实执行时间)
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 123\G

-- 4. 资源组(限制分析查询)
CREATE RESOURCE GROUP adhoc TYPE = USER VCPU = 0-1 THREAD_PRIORITY = 10;
SET RESOURCE GROUP adhoc FOR SELECT COUNT(*) FROM large_table;

八、效果指标与总结

8.1 调优效果汇总

优化手段典型提升幅度成本
SQL 优化(去掉 SELECT *)I/O ↓50%+
添加缺失索引查询快 10-1000 倍
联合索引优化回表↓,性能↑3-5 倍
批量操作吞吐↑10-50 倍
缓冲池调大命中率↑,磁盘 I/O↓
读写分离读容量↑3-10 倍
分库分表写入容量↑10-100 倍

8.2 调优检查清单

□ EXPLAIN 分析所有慢查询,type 避免 ALL/index
□ 索引覆盖高频查询,减少回表
□ 删除未使用和冗余索引
□ 批量操作替代逐条处理
□ WHERE 条件列避免函数包裹和隐式转换
□ LIKE 避免前导通配符
□ JOIN 字段类型一致且有索引
□ 分页查询使用延迟关联优化深分页
□ 配置 innodb_buffer_pool_size = 物理内存 50-70%
□ 根据业务设置日志刷盘策略
□ 开启慢查询日志,定期分析
□ 监控连接池使用率,避免打爆数据库

8.3 一句话总结

MySQL 调优性价比最高的是 SQL 优化和索引设计,其次是参数调优,最后才是架构改造。优先把前两步做扎实,80% 的问题都能解决。


📚 参考工具

工具用途
EXPLAIN查看执行计划
EXPLAIN ANALYZE(8.0)真实执行耗时
pt-query-digest慢日志分析
pt-index-usage索引使用分析
pt-online-schema-change在线 DDL
sys.schema_unused_indexes未使用索引
sys.schema_redundant_indexes冗余索引

作者注:本文是《中间件性能调优完全指南》的 MySQL 补充章节,与 Redis、Kafka、RabbitMQ、ELFK、连接池调优等内容共同构成完整的中间件调优知识体系。

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇