Skip to content
清晨的一缕阳光
返回

MySQL 内存参数配置与调优

核心概念

内存是 MySQL 性能的关键因素,合理的内存配置可以大幅提升性能。

MySQL 内存结构

MySQL 内存结构:
├── InnoDB Buffer Pool(最重要)
├── Query Cache(8.0 已移除)
├── 连接缓冲区
│   ├── Sort Buffer
│   ├── Read Buffer
│   ├── Join Buffer
│   └── ...
├── InnoDB Log Buffer
└── 其他缓冲区

内存分配原则

1. Buffer Pool 优先:分配 50-80% 物理内存
2. 避免过度分配:预留 20% 给操作系统
3. 连接缓冲区按需:不要设置过大
4. 监控调整:根据实际使用情况调整

InnoDB Buffer Pool

Buffer Pool 作用

Buffer Pool 是 InnoDB 最重要的内存区域,用于缓存数据和索引页。

Buffer Pool 结构:
├── Free List:空闲页
├── Flush List:脏页(需要刷盘)
├── LRU List:最近使用页
│   ├── Old Sublist(5/8)
│   └── Young Sublist(3/8)
└── Hash Table:页查找

Buffer Pool 配置

[mysqld]
# Buffer Pool 大小(物理内存的 50-80%)
innodb_buffer_pool_size = 8G

# Buffer Pool 实例数(减少锁竞争)
innodb_buffer_pool_instances = 8

# 预读页数
innodb_read_ahead_threshold = 56

# 随机预读
innodb_random_read_ahead = OFF

Buffer Pool 监控

-- 查看 Buffer Pool 使用情况
SHOW ENGINE INNODB STATUS\G

-- 关键指标:
-- BUFFER POOL AND MEMORY
-- Total memory allocated: 8589934592
-- Buffer pool size: 524287
-- Free buffers: 1000
-- Database pages: 520000
-- Old database pages: 192000
-- Modified db pages: 100
-- Pending reads: 0
-- Pending writes: LRU 0, flush list 0, single page 0

-- 使用 performance_schema
SELECT 
    pool_id,
    instance_id,
    pages_used,
    pages_free,
    dirty_pages,
    hit_rate
FROM information_schema.INNODB_BUFFER_POOL_STATS;

Buffer Pool 命中率

-- 计算命中率
SHOW STATUS LIKE 'Innodb_buffer_pool%';

-- 命中率公式:
-- 命中率 = (Innodb_buffer_pool_read_requests - Innodb_buffer_pool_reads) 
--         / Innodb_buffer_pool_read_requests * 100

-- 示例查询:
SELECT 
    (1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests) * 100 AS hit_rate
FROM information_schema.GLOBAL_STATUS
WHERE Variable_name IN ('Innodb_buffer_pool_reads', 'Innodb_buffer_pool_read_requests');

-- 理想命中率:> 99%

Buffer Pool 预热

-- 重启后预热 Buffer Pool
-- MySQL 8.0+ 支持 Buffer Pool 持久化

-- 开启持久化
SET GLOBAL innodb_buffer_pool_dump_at_shutdown = 1;
SET GLOBAL innodb_buffer_pool_load_at_startup = 1;

-- 手动导出热点页
SET GLOBAL innodb_buffer_pool_dump_now = 1;

-- 手动加载
SET GLOBAL innodb_buffer_pool_load_now = 1;

-- 导出特定比例的页
SET GLOBAL innodb_buffer_pool_dump_pct = 40;

Query Cache(8.0 已移除)

Query Cache 原理

MySQL 5.7 及之前版本支持,8.0 已移除。

Query Cache 工作流程:
1. 查询 SQL 哈希
2. 查找缓存
3. 命中:直接返回
4. 未命中:执行查询并缓存

Query Cache 配置(5.7)

[mysqld]
# 开启 Query Cache
query_cache_type = 1  # 0:OFF, 1:ON, 2:DEMAND

# Query Cache 大小
query_cache_size = 64M

# 最小缓存单元
query_cache_min_res_unit = 2K

# 缓存限制
query_cache_limit = 1M

Query Cache 问题

1. 锁竞争:全局锁,并发差
2. 失效频繁:表变更导致缓存失效
3. 碎片化:内存碎片

MySQL 8.0 移除原因:
- 并发场景性能差
- 维护成本高
- 实际收益低

连接缓冲区

Sort Buffer

Sort Buffer 用于 ORDER BY 和 GROUP BY 排序。
[mysqld]
# 排序缓冲区大小(每连接)
sort_buffer_size = 2M

# 排序临时表限制
max_heap_table_size = 256M
tmp_table_size = 256M

监控:

-- 查看文件排序次数
SHOW STATUS LIKE 'Sort%';

-- Sort_merge_passes: 归并次数(越少越好)
-- Sort_rows: 排序行数
-- Sort_range: 范围排序次数
-- Sort_scan: 全表排序次数

-- 优化目标:减少 Sort_merge_passes

Read Buffer

Read Buffer 用于顺序读取数据。
[mysqld]
# 顺序读缓冲区(每连接)
read_buffer_size = 2M

# 随机读缓冲区(每连接)
read_rnd_buffer_size = 1M

Join Buffer

Join Buffer 用于无索引的 JOIN 操作。
[mysqld]
# JOIN 缓冲区(每连接)
join_buffer_size = 4M

优化建议:

1. 优先添加索引,而不是增大 Join Buffer
2. Join Buffer 过大会浪费内存
3. 监控临时表使用

Thread Cache

Thread Cache 用于缓存空闲线程,减少创建开销。
[mysqld]
# 线程缓存数
thread_cache_size = 100

-- 监控线程创建
SHOW STATUS LIKE 'Threads%';

-- Threads_created: 创建次数(越少越好)
-- Threads_cached: 缓存数
-- 命中率 = Threads_created / Connections * 100

InnoDB Log Buffer

Log Buffer 作用

Log Buffer 用于缓存 redo log,定期刷盘。
[mysqld]
# Log Buffer 大小
innodb_log_buffer_size = 64M

# 刷新策略
innodb_flush_log_at_trx_commit = 1
# 0: 每秒刷盘,事务提交不刷
# 1: 每次提交刷盘(最安全)
# 2: 每次提交写 OS 缓存,每秒刷盘

# 日志文件大小
innodb_log_file_size = 512M
innodb_log_files_in_group = 2

Log Buffer 监控

-- 查看 Log Buffer 使用情况
SHOW ENGINE INNODB STATUS\G

-- Log 部分:
-- Log sequence number: 1234567890
-- Log buffered up to: 1234567890
-- Last checkpoint at: 1234500000
-- Pending checkpoint writes: 0

内存调优实践

调优步骤

1. 查看物理内存
   free -h

2. 计算可用内存
   可用内存 = 物理内存 * 0.8

3. 分配 Buffer Pool
   innodb_buffer_pool_size = 可用内存 * 0.7

4. 分配其他缓冲区
   连接缓冲区 = 可用内存 * 0.1

5. 预留操作系统
   操作系统 = 物理内存 * 0.2

配置示例

# 16GB 内存服务器
[mysqld]
# InnoDB Buffer Pool(8GB)
innodb_buffer_pool_size = 8G
innodb_buffer_pool_instances = 8

# Log Buffer(64MB)
innodb_log_buffer_size = 64M

# 连接缓冲区(每连接)
sort_buffer_size = 2M
read_buffer_size = 2M
read_rnd_buffer_size = 1M
join_buffer_size = 4M

# 线程缓存
thread_cache_size = 100

# 临时表
tmp_table_size = 256M
max_heap_table_size = 256M

# 连接数限制
max_connections = 200

内存计算公式

总内存需求 = 
  innodb_buffer_pool_size +
  (sort_buffer_size + read_buffer_size + join_buffer_size) * max_connections +
  innodb_log_buffer_size +
  其他内存

示例:
  8G (Buffer Pool) +
  (2M + 2M + 4M) * 200 (连接) = 1.6G +
  64M (Log Buffer) +
  其他 ≈ 10G

16GB 服务器配置 200 连接是安全的

内存监控

监控指标

-- 1. Buffer Pool 命中率
SELECT 
    (1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests) * 100 AS hit_rate
FROM information_schema.GLOBAL_STATUS;

-- 2. 内存使用情况
SELECT 
    VARIABLE_NAME, 
    VARIABLE_VALUE 
FROM information_schema.GLOBAL_STATUS 
WHERE VARIABLE_NAME LIKE '%memory%' 
   OR VARIABLE_NAME LIKE '%buffer%';

-- 3. 临时表使用情况
SHOW STATUS LIKE 'Created_tmp%';

-- Created_tmp_tables: 创建总数
-- Created_tmp_disk_tables: 磁盘临时表数
-- 磁盘临时表占比 = Created_tmp_disk_tables / Created_tmp_tables
-- 理想值:< 10%

Prometheus 监控

# MySQL 内存监控
groups:
  - name: mysql_memory
    rules:
      - alert: LowBufferPoolHitRate
        expr: (1 - mysql_global_status_innodb_buffer_pool_reads / mysql_global_status_innodb_buffer_pool_read_requests) * 100 < 95
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Buffer Pool 命中率低于 95%"
          
      - alert: HighTemporaryTables
        expr: mysql_global_status_created_tmp_disk_tables / mysql_global_status_created_tmp_tables > 0.1
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "磁盘临时表占比过高"

常见问题

问题 1:内存不足

症状:
- OOM Killer 杀死 MySQL
- 系统 swap 频繁

解决:
1. 减小 innodb_buffer_pool_size
2. 减小 max_connections
3. 减小连接缓冲区
4. 增加物理内存

问题 2:Buffer Pool 命中率低

症状:
- 命中率 < 95%
- 磁盘 IO 高

解决:
1. 增大 innodb_buffer_pool_size
2. 优化 SQL,减少全表扫描
3. 添加合适的索引
4. 预热 Buffer Pool

问题 3:临时表过多

症状:
- Created_tmp_disk_tables 占比高
- 查询慢

解决:
1. 增大 tmp_table_size 和 max_heap_table_size
2. 优化复杂查询
3. 添加索引避免文件排序

最佳实践

配置检查清单

[ ] innodb_buffer_pool_size = 物理内存的 50-80%
[ ] innodb_buffer_pool_instances = buffer_pool_size / 1G
[ ] 连接缓冲区不要设置过大
[ ] tmp_table_size = max_heap_table_size
[ ] 预留 20% 内存给操作系统
[ ] 开启 Buffer Pool 持久化
[ ] 监控 Buffer Pool 命中率

参考资料


分享这篇文章到:

上一篇文章
Redis 7.x 新特性详解
下一篇文章
MySQL 8.0 新特性