已采纳
回答正文
回复
讨论正文
该问题的正文、回答与讨论。
【背景】
MySQL 8.0.32 / InnoDB,生产主库 64C/256G,innodb_buffer_pool_size=128GB(3 个 instance),NVMe SSD(Intel P5510 3.84TB)。核心表 t_trade 约 3.1 亿行,数据文件 182GB(含 8 个二级索引),日常 OLTP 点查 QPS 约 2200(按 merchant_id + order_no 走唯一索引),buffer pool hit ratio 稳定 99.8%+,读 P99 在 4~7ms。
业务需要在线加一个覆盖索引:
ALTER TABLE t_trade ADD INDEX idx_merchant_status_ct (merchant_id, status, create_time), ALGORITHM=INPLACE, LOCK=NONE;
预期 INPLACE rebuild 阶段(内部 copy engine 逐行拷贝 + 重建所有二级索引)持续 25~40 分钟。
【现象】
DDL 执行期间(rebuild 阶段,SHOW PROCESSLIST 状态为 copy to tmp table),OLTP 读 P99 从 5ms 飙到 45~90ms,持续约 20 分钟后(DDL 结束后)自然回落。关键监控数据:
ALGORITHM=INPLACE, LOCK=NONE,SHOW ENGINE INNODB STATUS 无 MDL 等待,performance_schema.metadata_locks 无阻塞;Innodb_row_lock_waits 增量 < 5/min,无死锁记录;Innodb_buffer_pool_reads 从平时 300/min 涨到 18000~25000/min(持续 20 分钟),而 Innodb_buffer_pool_read_requests 基本不变(~120000/min)——说明不是 OLTP 请求量变了,而是同样的请求命中率掉了;Innodb_buffer_pool_pages_dirty 从 4% 涨到 11%(rebuild 产生大量脏页),Innodb_buffer_pool_wait_free 出现非零值(平时恒为 0);innodb_ddl_threads=8(默认值),rebuild 期间 8 个后台线程持续占满 6 个核(每个线程做 B-tree 插入),但 OLTP 查询线程的 CPU 时间没有明显增加。【已尝试的排查与排除】
Innodb_log_waits 增量为 0,Innodb_os_log_pending_writes < 10;Innodb_buffer_pool_wait_free 非零 + 物理读暴涨 + 命中率下降,三者同时出现;【已尝试的缓解方案】
SET GLOBAL innodb_ddl_threads=1(降到单线程 rebuild),重跑后 P99 峰值从 90ms 降到 35ms,但 DDL 总耗时从 30 分钟涨到 55 分钟;【期望得到的答案形态】
information_schema.INNODB_BUFFER_PAGE 或 performance_schema 的 memory 相关表,统计 rebuild 线程实际分配了多少页、逐出了多少 OLTP 热页?(我想拿到一个精确的"逐出量 vs. P99 尖刺幅度"的对应关系)innodb_buffer_pool_instances 配合某种调度?)innodb_ddl_threads 设为 2~3(而非 1)时,P99 尖刺和 DDL 总耗时之间有没有一个"性价比拐点"?有人做过这种 sweep 实验吗?innodb_ddl_max_dirty_pages 的参数可以限制 rebuild 产生的脏页总量?(我翻了 8.0.32 的文档没找到)merchant_id + status + create_time 这个覆盖索引拆成一张独立的"影子索引表",由应用层做 join,彻底绕开 DDL?Innodb_buffer_pool_wait_free 之外,还有没有更早的信号可以预警"OLTP 热页正在被逐出"?(比如 buffer pool 的 young/old 比例变化、Innodb_buffer_pool_pages_total 的增量速率?)希望有实战经验的朋友给一个可落地的方案对比,最好带具体的参数值和 P99 数据。