已采纳
回答正文
回复
讨论正文
该问题的正文与回答。
我负责一套 SaaS 多租户计费系统的核心库,MySQL 8.0.36(InnoDB,32C128G,NVMe,innodb_buffer_pool_size=96G)。核心表 t_billing_item 约 1.6 亿行,联合索引 idx_tenant_period (tenant_id, billing_period, status)。业务形态有个硬约束:每天 02:00–06:00 上游 ETL 批量灌入 3000–5000 万行(一次性 INSERT 完再跑聚合),白天是正常 OLTP 点查,P50 约 4ms。
批灌窗口内慢日志(long_query_time=1)从平时每秒 10–20 条飙到 300+ 条/秒,持续 2–3 小时。问题有两层:
slow_query_log_file 单晚涨到 40+ GB,轮转时 IO 抢占明显,白天 SHOW PROCESSLIST 里能看到 Log_slow 相关会话;innodb_stats_persistent_sample_pages=20),白天一条 WHERE tenant_id=? AND billing_period IN (?,?,?) 的点查,有的租户从走 idx_tenant_period 退化成了全表扫描(P99 从 8ms 涨到 2.1s),但慢日志里这条 SQL 根本没出现——因为它只在部分租户触发,且单条 2s 落在 1s 阈值边缘,被淹没在几千条同类噪音里。我们是在业务方投诉后手动 EXPLAIN 才发现的。long_query_time 从 1s 提到 5s,噪音降了 80%,但上面那种 2s 的退化查询直接漏掉了;log_slow_extra 相关开关:开了 log_slow_extra,但条目更多,噪音反而更密;performance_schema.events_statements_history:试过抓 DIGEST_TEXT + PLAN 字段(8.0.30+ 有 PLAN 列),但 history 表只有 10 条,抓不到跨小时窗口的计划变化;events_statements_history_long 开了,但表太大(单表 5000 万行),查起来本身就慢;ANALYZE TABLE:批灌后手动跑了一次,统计信息确实变了,但跑完到跑完之间(约 40s)的计划是新旧混杂的,不好观测;innodb_stats_persistent_sample_pages 从 20 调到 200:在测试库试过,ANALYZE TABLE 耗时从 ~30s 涨到 ~3min,但 1.6 亿行的大表上这个成本是否可接受、以及「调大采样」是否真能压住 ETL 后的计划漂移,我没有线上数据。EXPLAIN,比对 key / type / rows 三列,变化超过阈值就报警。但这个方案本身有成本(定时 EXPLAIN 会不会触发统计信息刷新?会不会和在线查询抢锁?),有没有人踩过这个坑?innodb_stats_persistent_sample_pages 在 1 亿+ 行表上调大的实际成本:你们线上有没有从 20 调到 100/200 的经历?ANALYZE TABLE 耗时增加了多少?是否出现「采样更准但优化器反而选错」的反例(因为统计信息更真实地反映了长尾分布,而优化器的 cost model 对长尾不友好)?optimizer_trace 太重、FORCE INDEX 要改代码、CLUSTERED INDEX 在 MySQL 里没有原生 hint。你们在生产上是怎么做「计划稳定性」的?是接受漂移后靠告警兜底,还是有更主动的手段(比如 optimizer_switch 的某些开关、或 optimizer_trace_features 的定向抓取)?不需要「理论上的最佳实践」,我想要:
innodb_stats_persistent_sample_pages 的,给一下 1 亿+ 行表上的 ANALYZE TABLE 耗时数据,以及有没有反例。空谈「统计信息要准」「计划要稳」的就不必了——我想知道你的「准」和「稳」具体是怎么量化的。
(引用:id=1859《EXPLAIN 的 rows 估 120、实际扫 1800 万行:InnoDB 统计信息只采样 20 页导致优化器选错索引的排查》和 id=208《同一索引同一 SQL,凌晨 P99 从 15ms 飙到 3.4s:统计信息漂移的根因与修复》——这两篇讲清了机制,但我卡在「ETL 窗口内怎么实时发现 + 怎么兜底」这一环。)