大表分批 UPDATE 锁等待超时,batch size 和间隔到底怎么定才不崩
该问题的正文、回答与评论。
背景:MySQL 8.0.36,InnoDB,一张约 2 亿行的用户积分流水表 point_flow,主键 id,二级索引 idx_uid (uid)、idx_uid_time (uid, create_time)。业务侧有一个对账修复任务,需要按 uid 范围批量更新 status 字段(从 pending 改成 confirmed),单批 UPDATE ... WHERE id BETWEEN x AND y AND status='pending'。
现象:batch size 设为 5000、批间 sleep 50ms 时,高峰期会出现 Lock wait timeout exceeded(innodb_lock_wait_timeout=50s),且同一 uid 的并发写请求被卡住;batch size 降到 500 后锁等待消失,但整个修复任务要跑 6 个多小时,业务侧不接受。另外跑完 1 亿行更新后,表体积膨胀明显,SHOW TABLE STATUS 里 Data_free 涨到 40GB+。
已尝试:1) 调大 innodb_lock_wait_timeout 到 120s,结果不是解决而是把超时时间拉长,期间业务写堆积更严重;2) 改成按 uid 分段 + 每段内按 id 分批,锁范围缩小了但总耗时反而更长(uid 分布不均,热门 uid 单段上千万行);3) 考虑过 pt-online-schema-change 思路做影子表迁移,但这是数据修复不是 DDL,思路对不上。
期望得到的答案:1) batch size、批间隔、锁粒度的量化关系,有没有经验公式或压测方法;2) 对 uid 分布严重倾斜的场景,分批策略该怎么设计(按行数、按时间、还是按 uid 分桶);3) 表膨胀的清理方案,OPTIMIZE TABLE 在线执行的风险和替代手段。