写在开头
以下的每个参数详细拉开来讲都可以开一篇文章,篇幅所限,我这里会给出一些基本的描述以及建议的参数,详细了解的请自行百度
知其然才能知其所以然
如果只是把参数根据当前服务器的配置列出来,自然是最快完成任务的方式,但这种方式我们始终不知道是为什么要这么配置,故以下会先科普一些底层的知识,比如innodb 缓冲池,redo log 缓冲区, log buffer,redo log 文件这些东西到底是什么玩意,如果要完全理解可能需要系统的学习mysql底层的知识,因为背后牵扯到 innodb 存储引擎的内部机制:数据是怎么存储、怎么读取、怎么保证事务一致性的。
本文会尽可能用一些通俗的比喻来让你理解
内存池相关
innodb_buffer_pool_size innodb_buffer_pool_instances innodb_log_buffer_size
innodb_buffer_pool_size
这是 innodb 缓冲池大小,作用就是把数据页、索引页、插入缓存、锁信息等都放到内存里,减少磁盘随机 i/o。
**建议:**分配为物理内存的 60%~75% 左右。
如果 buffer pool 太小,查询就频繁读磁盘,性能会雪崩。
- 就像一座“大冰箱”,里面放着你常用的菜(数据页、索引页)。
- 查询数据时,如果“冰箱”里有(命中缓存),就直接从内存里取,快得飞起。
- 如果没有,就得下楼去超市(磁盘),再顺便搬进冰箱,下次就快了。
innodb_buffer_pool_size = 冰箱有多大。
innodb_buffer_pool_instances
这个参数是把大 buffer pool 分片,避免多线程并发访问时锁竞争。
mysql 8 默认就会根据 innodb_buffer_pool_size 自动调整,一般每个 instance 至少要有 1g~2g。
**建议:**比如innodb_buffer_pool_size=24g,innodb_buffer_pool_instances就要为16左右,这样 24g/16 = 1.5g 每个实例,正好在推荐范围里。
如果实例数太少,大锁竞争严重;太多则浪费管理开销。
innodb_buffer_pool_instances = 冰箱有几个独立的格子,防止大家挤着抢同一个门。
innodb_log_buffer_size
这是 redo log 缓冲区,事务在提交前会先写到 log buffer,然后再刷到 redo log 文件。
**建议:**默认一般是 16m 或 128m。如果太小事务中间就被迫刷盘。如果太大但是业务系统事务都很小(简单的单行 insert/update),太大没意义意义,可能浪费内存。
要讲清楚这个配置,就要科普另一个点
写数据不能直接怼磁盘
- 事务更新数据时,mysql 不能每条更新都立刻写磁盘,那样会被磁盘 i/o 拖死。
- 于是 innodb 使用 write-ahead logging(先写日志,再写数据) 的套路。
redo log(重做日志)
- 就像一本“日记本”,你在里面记下:今天把账本第 8 页的金额加了 100。
- 这个日记本在磁盘上是循环写的 redo log 文件。
- 如果数据库突然崩了,重启时可以根据 redo log 把内存里没来得及写的数据“重做一遍”,保证事务不丢。
redo log buffer(日志缓冲区)
- 写日记之前,先把草稿写在一个小白板(内存 buffer)。
- 等凑够一批或者事务提交了,再一次性抄到真正的日记本(redo log 文件)。这样减少了频繁刷盘。
所以:
- innodb_log_buffer_size = 白板有多大。事务很大时,白板小了就得频繁往日记本抄,拖慢速度。
- 拿一个事务的完整流程举例
update account set balance = balance - 100 where id = 1;
- innodb 在 buffer pool 找到 account 表里 id=1 的数据页(如果不在,就从磁盘读进来)。
- 在内存里修改数据页,balance 减 100。
- 把“修改过的数据”记录到 redo log buffer(白板)。
- 事务提交时,把 redo log buffer 刷到磁盘上的 redo log 文件。
- 数据页什么时候刷回磁盘?不一定立刻,可能延迟(后台刷脏页)。
这样做的好处:
- 日志先落盘,保证崩溃后还能恢复。
- 数据页延迟落盘,提高性能。
所以:innodb_log_buffer_size:白板大小,决定写日志时事务大不大会卡顿。
写日志redo log & 刷盘相关
innodb_redo_log_capacity innodb_flush_log_at_trx_commit innodb_flush_log_at_timeout sync_binlog
这几个参数都是 “日志系统的节奏控制器”,决定了 mysql 写日志的安全性 vs 性能 取舍。咱们还是延续“日记本”的类比来解释:
innodb_redo_log_capacity
这相当于 redo log 文件的 总大小(循环写)。redo log 记录了事务的修改操作,保证崩溃恢复。
它是循环写的,就像一本日记本,写满后会从头覆盖。
- 容量大 → 能容纳更多脏页(dirty page),后台写盘压力小,适合写入量大的场景。
- 容量小 → 脏页必须频繁被刷回磁盘,否则 redo log 会被撑爆。
建议: 几 gb 到几十 gb 都可以,看磁盘和 workload。我一般配 4g,算比较中等偏大的设置了,oltp(高并发事务)场景挺合适。普通web系统配置为1-2g就够
innodb_flush_log_at_trx_commit
控制了事务提交时 redo log(重做日志)的刷盘策略,有3个值,
- 0:每秒才统一刷一次盘。性能最好,掉电风险最大。
- 1(默认):每次事务提交,都会把 redo log buffer 写到 操作系统缓存,再 fsync(真正落到磁盘)。最安全,最慢。
- 2:每次提交只写到 操作系统缓存,不强制 fsync。由操作系统自己决定何时刷到磁盘。安全性差一点,但性能好很多。
建议: 一般业务系统设置为2
| 业务类型 | 推荐值 | 理由 |
|---|---|---|
| 金融、支付、订单 | 1 | 数据不丢是底线 |
| 普通 web 应用 | 2 | 性能与安全的折中 |
| 日志、监控、分析 | 0 | 可接受数据丢失 |
这个一秒可以通过参数:innodb_flush_log_at_timeout 来配置,默认值是1
sync_binlog
这是 binlog(归档日志)的同步策略。binlog 是逻辑日志,用于复制和恢复。
- 取值 1:每次事务提交都 fsync binlog → 最安全,但 i/o 压力大。
- 取值 0:事务提交时只是写到 os 缓存,不强制落盘 → 崩溃可能丢 binlog。
- 取值 n (>1):每 n 次事务才强制刷一次。
后台 i/o、清理线程和并发限制
innodb_write_io_threads = 16 innodb_read_io_threads = 16 innodb_purge_threads = 4 innodb_thread_concurrency = 0
innodb_write_io_threads
- 这是 后台写 i/o 线程数。
- 负责把内存里的 脏页(dirty page) 刷回磁盘。
- 负责写 doublewrite buffer、合并 insert buffer 等写操作。
- 线程越多 → 能同时提交更多写请求。
- 但如果磁盘(比如 ssd)并发能力有限,开太多线程也没用,还会增加调度开销。
建议: 4-16。在现代服务器 + ssd 场景,比如16 个写线程是比较激进的配置,适合高写入压力的系统,如果是hhd硬盘,设置为4足够,设置多了会增加线程调度开销。
innodb_read_io_threads
- 这是 后台读 i/o 线程数。
- 负责预读数据页(read-ahead)和处理用户线程发起的磁盘读请求。
- 线程数越多,能同时发起更多读请求。
- 同样受磁盘并发能力限制。
建议: 一般 4-8 足够。
innodb_purge_threads
- 这是 purge(清理)线程数。
- purge 负责清理 undo log(事务提交后遗留的旧版本)。
- 如果 purge 太慢,undo log 会堆积,导致表膨胀、性能下降。
- 多线程 purge 可以更快回收空间、减少 undo 膨胀。
建议: 一般设置为2就够了,高并发场景可以考虑设置为4
临时表全内存
temptable_max_ram tmp_table_size max_heap_table_size internal_tmp_mem_storage_engine
这几个参数都跟 临时表 有关,而临时表就是 mysql 在执行 sql 时的“临时工地”,用来放中间结果。
比如:group by、order by、distinct、union、join 等操作,如果不能直接用索引,就可能生成临时表。
temptable_max_ram
- 这是 mysql 8 新引入的参数,专门限制 **temptable 引擎(内存临时表)**的最大内存占用。
- temptable 是 mysql 8 默认的内部临时表引擎(之前默认是 memory 引擎)。
- 当单个临时表大小超过这个值时,会溢出到磁盘(存成 on-disk temporary table)。
tmp_table_size / max_heap_table_size
内存临时表的大小上限。复杂查询(group by、join、order by)结果可能要放临时表。超过大小会落到磁盘 → 性能急剧下降。
这两个值建议设置为一样的,对齐限制,避免临时表因为两个参数不一致导致意外落盘或报错。
最佳配置
普通的web类后台管理系统
这类系统的普遍情况是:很少有高并发,但存在很多复杂,多张表关联查询
- 16g内存最佳配置
# ------------------------- # innodb 缓冲池(核心参数) # ------------------------- innodb_buffer_pool_size = 8g # 给 innodb 分配 50% 内存,留给系统和其他 mysql 组件 innodb_log_buffer_size = 128m # 日志缓冲大小,适合复杂查询场景 innodb_max_dirty_pages_pct=50 # 控制 脏页(dirty page)在缓冲池中允许的最大比例。推荐 50~60,平衡刷盘压力和性能。 # ------------------------- # redo log & 刷盘策略(安全优先) # ------------------------- innodb_redo_log_capacity = 1g # 合理大小,防止频繁刷新 innodb_flush_log_at_trx_commit = 2 # web系统配置为2足够 innodb_flush_log_at_timeout = 1 # 最多延迟 1 秒刷盘 # 默认200,如果ssd,调整为5000~20000 innodb_io_capacity=200 # ------------------------- # 临时表优化(多表关联查询关键) # ------------------------- temptable_max_ram = 512m tmp_table_size = 512m max_heap_table_size = 512m internal_tmp_mem_storage_engine = temptable # ------------------------- # 连接和缓存(低并发适度) # ------------------------- max_connections = 200 thread_cache_size = 50 table_open_cache = 2000 table_definition_cache = 1000 open_files_limit = 65535
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论