已采纳
回答正文
回复
讨论正文
该问题的正文与回答。
我负责一套商户结算系统(MySQL 8.0.32 / InnoDB,单主 16C64G,buffer_pool 32G)。核心表 t_trade_detail 约 2800 万行,40 列宽表(大量 VARCHAR(128) 的业务字段 + DECIMAL 金额列),SHOW TABLE STATUS 单表约 42GB,现有 5 个二级索引。写入画像:INSERT ~600 QPS + 状态 UPDATE ~200 QPS。
一条高频查询(对账明细列表,~300 QPS):
SELECT merchant_id, status, order_no, amount, created_at, pay_channel, settle_status
FROM t_trade_detail
WHERE merchant_id = ? AND status IN ('SETTLED','SETTLING')
ORDER BY created_at DESC
LIMIT 20
现状走 idx_merchant_status_time (merchant_id, status, created_at),EXPLAIN:type=range,key 正常,单次 P50 ~25ms、P99 ~180ms(并发 300 QPS 下抖动明显)。performance_schema 里回表相关等待占比不低,我怀疑瓶颈在回表随机 I/O——宽表行宽大,一次回表可能跨多个 extent,buffer pool 装得下索引却装不下数据页(和 #2417 描述的「半命中」形态很像)。
idx_merchant_status_time_cover (merchant_id, status, created_at, order_no, amount, pay_channel):本地压测单条 P99 降到 ~8ms。但顾虑:这条索引要携带 order_no VARCHAR(64) + amount DECIMAL + pay_channel VARCHAR(32),索引体积预估膨胀到主表数据量的 30~40%(~15GB),且 800 QPS 写入每个事务都要维护它——写放大在 #2605 的讨论里大家算过,但我没有针对宽表大行宽场景的量化数据。不是「要不要加覆盖索引」的定性讨论(#878 和 #2417 已经把账单讲清楚了),而是求一个可复现的量化判据,具体想问三点:
performance_schema / sys 库里拆出来?我目前只能看 innodb_buffer_pool_reads 总量,分不清是索引页 miss 还是数据页 miss——有没有比看总命中率更细的观测手段(比如按表拆的 file_ios 或 table_iostat)?order_no 塞进索引(半覆盖,省掉最大的 VARCHAR 回表成本)、或者把列表页的 pay_channel 拆到关联小表、或者用 innodb_buffer_pool 的 sub-pool 隔离热数据页?你们线上走过哪条?期望的回答:有具体观测 SQL / 压测脚本 / 数据点支撑的量化判据,最好能给出一个「代入我的参数(2800 万行 / 40 列 / 800 写 QPS / 300 读 QPS / 行宽 1.5KB)就能出结论」的决策路径。