3.2 核心器官:InnoDB存储引擎


3.2 核心器官:InnoDB 存储引擎

本节摘要:InnoDB 由缓冲池、change buffer、redo log、undo log、binlog 协同工作。缓冲池决定读得快不快,redo 保证崩溃不丢,undo 支撑回滚与多版本读。看懂这套器官系统,一半的"玄学性能问题"会变成可解释的生理现象。

缓冲池:短期记忆

磁盘读比内存慢几个数量级,InnoDB 把热点数据页缓存在内存的缓冲池里:

SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW STATUS LIKE 'Innodb_buffer_pool_read%';

Innodb_buffer_pool_reads(穿透到磁盘的次数)除以 Innodb_buffer_pool_read_requests(总请求)就是未命中率。命中率长期低于 99%,说明内存不够装热点数据,这是加内存最无可争议的信号。

"重启后变慢、跑一会儿又快了"——这是缓冲池被清空后重新预热的现象,不是玄学。

两本日记:redo 与 undo

  • redo log:物理日志,记"哪个页改了什么"。先写日志再慢慢刷数据页,崩溃后重放即可恢复,这叫先写日志原则,是高性能写入的根基
  • undo log:逻辑日志,记"怎么改回去"。回滚靠它,多版本读(第 5 章 MVCC)也靠它

binlog 在 Server 层,归档、复制用的都是它。一次 UPDATE 的提交要把 redo 与 binlog 都写盘,两阶段提交保证两本账对得上。

SHOW ENGINE INNODB STATUS\G -- 一页 InnoDB 体检报告

图:一次 UPDATE 在器官间的流转

⚠️ 常见坑:长事务不提交,undo 无法清理,表空间像充气一样膨胀。看到 ibdata 或 undo 表空间疯涨,先查有没有挂了好几天的旧事务。

缓冲池的管理哲学:冷热分离

缓冲池不是一个大锅饭,InnoDB 把它分成新生代与老生代两个区域(类似缓存系统常见的分代设计)。新读入的页先进"新生代"浅尝辄止,被再次访问才有资格晋升"老生代"长期驻留。这个设计专治一个真实病症:一次性大扫描(比如月底全表跑一遍的对账 SQL)会把整个缓冲池的热数据冲刷出去,扫完之后全库的查询集体变慢——缓存污染。有了分代,一次性访问的页只污染新生代那一小块,核心热数据安然无恙。

-- 分代相关参数(默认新生代占 3/8,一般不必动) SHOW VARIABLES LIKE 'innodb_old_blocks_pct'; SHOW VARIABLES LIKE 'innodb_old_blocks_time';

实际调优中更常做的不是动分代参数,而是把"大扫描"从主库挪走:报表查询去从库、去分析引擎,让主库缓冲池专心服务 OLTP 热点。这又回到了第 1 章的选型哲学——很多参数问题,最后都是架构问题。

脏页与刷盘节奏

修改先落在缓冲池里的页上,这些页与磁盘不一致,就是脏页。脏页迟早要刷回磁盘,什么时候刷、刷多快,由 redo log 的剩余空间倒逼:redo 是环形复用的,写满之前必须把对应脏页刷下去腾地方。于是有一个反直觉现象——系统越空闲、突然一波写入高峰时,可能因为 redo 追平而出现刷盘抖动,表现为周期性的写入卡顿:

-- 观察脏页水位与刷盘活动 SHOW STATUS LIKE 'Innodb_buffer_pool_dirty'; SHOW STATUS LIKE 'Innodb_data_writes'; SHOW STATUS LIKE 'Innodb_redo_log_%';

6.3 节会把 innodb_redo_log_capacity 的容量规划讲透,这里先记住因果链:redo 容量小,写入高峰就容易被"该刷盘了"打断;容量给足,高峰期的写入可以攒着慢慢刷。

一次宕机恢复的推演

把几个器官串起来的最好方式是推演一次崩溃恢复。实例意外断电重启:先把 redo 里已提交未刷盘的重放(前滚),数据恢复到崩溃瞬间的已提交状态;未提交事务靠 undo 反向执行(回滚)。这个推演解释了两件日常事:为什么崩溃后重启要"恢复几分钟",那是在重放 redo,写入越猛重放越久,所以维护重启也要预留窗口;为什么提交了的数据绝不丢,redo 在 COMMIT 时已落盘,磁盘上的数据页旧一点没关系。

顺带一个容易混淆的辨析:redo 是 InnoDB 的物理日志,binlog 是 Server 层的逻辑日志。一个管崩溃恢复,一个管复制与归档,两本账由两阶段提交对齐。第 7 章备份恢复用的完全是 binlog 这本账,到时候回来对照。

change buffer:二级索引写入的缓冲垫

change buffer 是容易被忽略的第四个器官。二级索引页不在缓冲池里时,插入操作不必立刻把页读进来——先把"这页将来要插入什么"记在 change buffer 里,等页被自然读到时再合并。这对写多读少的表(日志表、流水表)是显著减负:省掉大量为插入而做的随机读。

它有两个边界。唯一索引用不了:唯一性校验必须读页,缓冲没有意义,这也是"流水表索引别建唯一"的机理依据。写后立刻读的场景用不上:页马上要被读,缓冲白记还得合并,等于多一道手续。所以 change buffer 的受益场景画像非常清晰——大批量写入、查询集中在少数热页的表,比如按时间查最近数据的日志流水。

SHOW VARIABLES LIKE 'innodb_change_buffer%'; SHOW STATUS LIKE 'Innodb_buffer_pool_wait_free'; -- 缓冲池紧张的旁证

本节要点回顾

  • 缓冲池命中率:低于 99% 是加内存的硬指标,重启变慢是预热现象
  • redo 保崩溃恢复,undo 保回滚与多版本,binlog 保复制与归档
  • 长事务是公害:占锁、堵 purge、撑大 undo

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U