数据库高可用与容灾实战(8):亿级大表清理与归档:pt-osc 与 gh-ost 原理实战

发布时间:2026/9/30 11:22:51

数据库高可用与容灾实战(8):亿级大表清理与归档:pt-osc 与 gh-ost 原理实战 随手一条 DELETE 就是一场事故第 7 篇教了怎么把误删找回来这一篇讲怎么不制造那次误删之后更常见的事故清理亿级历史数据。业务方的需求听起来只有一句话——“把三年前的订单删掉”落到数据库上却是最凶险的一类操作一条DELETE FROM orders WHERE created_at ...圈住两亿行undo 表空间被撑到磁盘报警、一个不可拆分的巨型事务让从库延迟爬到小时级第 1 篇 MTS 栅栏的极端形态、回滚比删除更慢、删完磁盘还不归还第 1 篇的高水位。正确的形态是一个工程分块、限流、归档先行、在线重建收尾。本篇把清理拆成分块删除动力学和在线重建工具账两段各做一个模拟。为什么删除必须是项目而不是语句先立三条约束。约束一事务要小。InnoDB 的回滚段按事务累积亿行级事务的 undo 既撑磁盘又拖慢 purge中途 kill 还要花时间回滚所以按主键或索引范围切成万行一块每块一个短事务。约束二从库要追得上。删除的行事件同样要进 binlog、在从库逐个回放块间节奏必须由从库延迟驱动而不是拍脑袋的sleep 0.5——这就是限流器的全部哲学。约束三数据要有人接。清理和归档是同一件事的两面INSERT INTO archive_db.orders SELECT ...按同一批主键与DELETE放进同一个事务两份账要么都动要么都不动块大小下查出的行集与删掉的行集必须严格同界用WHERE id BETWEEN a AND b而不是LIMIT二义定位。工程上这套循环叫分块归档删除pt-archiver是它的现成封装--limit定块、--commit-each定事务、--sleep/--max-lag定限流、--where定圈选。实验一块与块之间睡多久从库说了算两百万行待清、每块一万行主库侧每块 0.5 秒写完从库回放速率只有四分之一行锁与二级索引维护的折损。对比固定节奏每块睡 0.5 秒与延迟超过 0.2 秒就暂停派发下一块的自适应策略。TOTAL,CHUNK2_000_000,10_000MR,AR20_000,5_000# 行/秒: 主库产出 vs 从库回放LOW0.2# 自适应恢复线(延迟秒): 高于它就只追不写defsimulate(fixed_sleepNone,adaptiveFalse):dt0.05tlagstalepeak0.0remaining,write_left,napTOTAL,0.0,0.0whileremaining0orwrite_left0ornap1e-9orlag1e-9:if(write_left1e-9andnap1e-9andremaining0and(notadaptiveorlag/ARLOW)):cmin(CHUNK,remaining)# 开写下一块write_left,remainingc,remaining-cifwrite_left1e-9:stepmin(dt,write_left/MR)rowsmin(write_left,MR*step)write_left-rows lagrows-AR*step# 从库同时在追, 但追不上ifwrite_left1e-9andfixed_sleepisnotNone:napfixed_sleepelifnap1e-9:stepmin(dt,nap)nap-step lagmax(0.0,lag-AR*step)else:stepdt# 空转: 只消化欠账lagmax(0.0,lag-AR*step)tstepiflagAR:stalestep peakmax(peak,lag/AR)returnt,peak,stale naivesimulate(fixed_sleep0.5)adasimulate(adaptiveTrue)print(固定节奏(每块睡0.5s): 全程 %.0fs, 峰值延迟 %.0fs, 读陈旧(1s)时长 %.0fs%(naive[0],naive[1],naive[2]))print(自适应限流(延迟%.1fs 暂停派发): 全程 %.0fs, 峰值延迟 %.1fs, 读陈旧时长 %.0fs%(LOW,ada[0],ada[1],ada[2]))print(理论边界: 主库裸速 %.0fs, 从库回放上限 %.0fs —— 自适应逼近后者, 这就是可陪跑的价格%(TOTAL/MR,TOTAL/AR))print(每块 一个短事务(INSERT 归档表 DELETE 同批行, 同事务保证两份账一致), 块间看延迟脸色决定睡不睡)运行输出固定节奏(每块睡0.5s): 全程 400s, 峰值延迟 200s, 读陈旧(1s)时长 399s 自适应限流(延迟0.2s 暂停派发): 全程 400s, 峰值延迟 1.7s, 读陈旧时长 180s 理论边界: 主库裸速 100s, 从库回放上限 400s —— 自适应逼近后者, 这就是可陪跑的价格 每块 一个短事务(INSERT 归档表 DELETE 同批行, 同事务保证两份账一致), 块间看延迟脸色决定睡不睡两种策略总时长都是 400 秒——因为瓶颈本来就在从库回放速率固定节奏只是把账欠到最后集中爆。区别在形状固定节奏的延迟一路线性爬到 200 秒期间所有从库读都是陈旧数据5 秒熔断线早在第 5 篇就把它打回主库主库平白多扛全部读流量自适应把峰值压在 1.7 秒读陈旧时长少一半。另一个反直觉点删除任务主库很快从来不是好事限流器的目标函数应该是从库延迟上界 业务高峰避让而不是今晚必须删完。实验二表瘦身收尾的在线重建——pt-osc 与 gh-ost 的账删完历史数据表文件还是三百行的体量高水位不降第 1 篇。收尾手段是在线重建pt-osc 与 gh-ost 都走建 ghost 表 → 搬保留行 → 追增量 → RENAME 交换分歧在增量怎么搬。pt-osc 在原表挂三个触发器每笔业务写顺手往 ghost 记一份gh-ost 不碰原表伪装成从库消费 binlog 行事件异步重放到 ghost。骨架相同窗口期那几百万笔业务写的去向就是两案的量级差异算给你看。KEEP,DISCARD100_000_000,200_000_000# 保留新数据, 丢弃历史数据MIGRATION_MIN240# 拷贝窗口(分钟): 100M 行 ÷ 拷贝速率BIZ{insert:5_000_000,update:2_000_000,delete:1_000_000}# 窗口内业务写ROW_KB0.35# 平均行大小copy_opsKEEP biz_opssum(BIZ.values())pt{ghost写入:copy_opsbiz_ops,原表额外写(触发器):biz_ops,ghost额外读(重拷被改块):int(biz_ops*0.18)}gh{ghost写入:copy_opsbiz_ops,原表额外写(触发器):0,ghost额外读(重拷被改块):int(biz_ops*0.18)}print(拷贝主体两案相同: %s 行写入 ghost; 差异在增量搬运%format(copy_ops,,))forname,min((pt-osc,pt),(gh-ost,gh)):extram[原表额外写(触发器)]m[ghost额外读(重拷被改块)]print(%-7s: 原表额外写 %s 行, ghost 额外读 %s 行, 总增量 %s 行(约 %.1fGB 行宽)%(name,format(m[原表额外写(触发器)],,),format(m[ghost额外读(重拷被改块)],,),format(extra,,),extra*ROW_KB/1_048_576))pt_trig_latency0.4# 每笔业务写多一条触发器 insert 的开销(ms)print(\n业务侧观测: pt-osc 窗口内每笔写 %.1fms 触发器开销(8M 笔累计 %.0f 分钟 CPU); gh-ost 业务写路径不变%(pt_trig_latency,biz_ops*pt_trig_latency/1000/60))print(切换动作: pt-osc RENAME 元数据级交换(毫秒, 但需要拿得住 MDL); gh-ost cut-over 锁原表-追平binlog-交换-解锁(默认 max-lag 节流, 秒级停顿可预期))print(失败面: pt-osc 中途有人 ALTER 原表直接报错回滚; 触发器占用列额度, 宽表可能建不下)print( gh-ost 依赖 binlog: 保留时长必须 拷贝窗口 %d 分钟, binlog_row_image 必须 FULL%MIGRATION_MIN)archive_rowsDISCARDprint(\n别忘了归档: ghost 只装留下的 %s 行, 被丢弃的 %s 行不在任何一方的命运里——%(format(KEEP,,),format(archive_rows,,)))print( 要么先跑实验A的分块INSERT归档DELETE把 %s 行搬去归档库, 再触发重建;%format(archive_rows,,))print( 要么重建前在 ghost 定义里保住全部行, 切换后再分块清——两种都要把数据去向写进变更单)lag12.0print(节流联动: gh-ost --max-lag-millis 与实验A同一根线: 从库延迟 %.0fs 时拷贝器自动降速, 业务读从库不受伤%lag)运行输出拷贝主体两案相同: 100,000,000 行写入 ghost; 差异在增量搬运 pt-osc : 原表额外写 8,000,000 行, ghost 额外读 1,440,000 行, 总增量 9,440,000 行(约 3.2GB 行宽) gh-ost : 原表额外写 0 行, ghost 额外读 1,440,000 行, 总增量 1,440,000 行(约 0.5GB 行宽) 业务侧观测: pt-osc 窗口内每笔写 0.4ms 触发器开销(8M 笔累计 53 分钟 CPU); gh-ost 业务写路径不变 切换动作: pt-osc RENAME 元数据级交换(毫秒, 但需要拿得住 MDL); gh-ost cut-over 锁原表-追平binlog-交换-解锁(默认 max-lag 节流, 秒级停顿可预期) 失败面: pt-osc 中途有人 ALTER 原表直接报错回滚; 触发器占用列额度, 宽表可能建不下 gh-ost 依赖 binlog: 保留时长必须 拷贝窗口 240 分钟, binlog_row_image 必须 FULL 别忘了归档: ghost 只装留下的 100,000,000 行, 被丢弃的 200,000,000 行不在任何一方的命运里—— 要么先跑实验A的分块INSERT归档DELETE把 200,000,000 行搬去归档库, 再触发重建; 要么重建前在 ghost 定义里保住全部行, 切换后再分块清——两种都要把数据去向写进变更单 节流联动: gh-ost --max-lag-millis 与实验A同一根线: 从库延迟 12s 时拷贝器自动降速, 业务读从库不受伤选型口径可以背下来高写入热表优先 gh-ost原表零侵入、可控节流、可暂停重试代价是它是个常驻进程、要求 ROW 全镜像 binlog 且保留期覆盖整个窗口pt-osc 赢在无外部依赖、纯 SQL 可做、rename 原子性干脆输在触发器把每笔写放大一次、且中途 DDL 直接翻车。两案共同的硬要求表要有主键或唯一键做分块坐标ghost 需要约一倍的磁盘余量rename 后旧表至少留置一个观察期再删。更根本的解法与变更清单时间序大表订单、流水建表即分区按月 RANGE清理退化为ALTER TABLE ... DROP PARTITION秒级、零复制、空间立即归还——所有分块删除都是在为没分区还债。冷数据有查询需求就上归档库分块删除的目的地是归档实例可低规格、可换引擎别删进无人认领的 binlog。变更单四件套块大小与限流阈值、从库延迟熔断线多少秒暂停/恢复、归档去向与行数对账口径、中途暂停与续跑方案断点已处理的最大主键。避开三样时刻业务高峰、备份窗口第 6 篇的 XtraBackup 与重建抢 IO、切换演练日。删除类变更配守恒对账主键段行数、金额字段总和、归档表行数三者勾稽任一不平立即停手。清理与重建解决的是单实例内的历史包袱。但整个系列反复假设的机房还活着终将被打破当一整个机房断电、网络孤岛、或 Region 级不可用时高可用体系怎么有计划地扛过去而不是碰运气下一篇《数据库高可用与容灾实战9机房级故障演练混沌工程怎么做到可回滚》。参考来源Percona Toolkit Documentationpt-online-schema-changehttps://docs.percona.com/percona-toolkit/pt-online-schema-change.htmlPercona Toolkit Documentationpt-archiverhttps://docs.percona.com/percona-toolkit/pt-archiver.htmlGitHubgithub/gh-osthttps://github.com/github/gh-ostMySQL 8.0 Reference ManualPartitioning of Tableshttps://dev.mysql.com/doc/refman/8.0/en/partitioning.htmlGitHubpercona/percona-toolkithttps://github.com/percona/percona-toolkit本系列已结集为免费专栏数据库高可用与容灾实战从主从复制到机房级演练进阶推荐付费专栏Python 自动化接单实战从脚本到第一单限时 ¥9.9首篇免费试读
延伸阅读

更多相关文章

2026/9/30 11:22:51

计算机网络基础怎么学?TCP/IP、子网划分与Wireshark抓包实战

计算机网络基础这个话题,说起来百分之九十的学生第一反应都是“背”——背OSI七层、背TCP三次握手、背各种协议端口号。但真正上了考场或者打开Wireshark一看,很多人就懵了:这题到底在考哪一层?这个报文我为什么看不懂&#xff1f…

2026/9/30 11:17:49

SSM实战:儿童教育PTC管理系统设计与开发全解析

如果你最近也在找一个能写进简历、又能顺利通过课程验收的Java项目,我强烈建议你研究一下“儿童教育在线学习系统PTC管理系统”这种题材。它表面上是普通的SSM三件套项目——Spring管理业务对象、SpringMVC处理请求分发、MyBatis操作数据库,但真正有意思…

2026/9/30 11:17:49

讲解设备验厂看哪些环节,无线讲解器产线与品控的技术清单

海外客户到厂验线,跟审厂、跟单都不太一样。他们不是来看产能数字的,是来把工艺细节摊开问的。问什么、看什么、凭什么判断,其实有一条相对固定的路径。我把这条路径整理成一份清单,给需要接待验厂或者准备海外交付的人做参考&…

2026/9/30 12:13:03

GO [ 泛型 ]

泛型 前面我们已经学习了 Go 的变量、常量、数据类型、输入输出、条件控制、切片、字符串、映射表、指针、结构体、函数、方法、接口、类型、错误、文件和反射。接下来学习 Go 语言中一个很重要、也最容易被“写复杂”的能力:泛型(Generics)…

2026/9/30 12:13:03

STM32上电启动全流程解析:从复位向量到RTOS第一个任务

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/30 12:13:03

国产车规MCU首次量产主动悬架:从工程视角拆解核心技术

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/30 12:13:03

DeepSeek-VL2在证券结算对账中的多模态核验实战

简介:本资源是一份面向金融科技从业者、量化系统开发工程师及AI模型落地工程师的深度技术方案文档,聚焦证券交易结算对账这一高精度、强合规场景,系统性提出基于DeepSeek-VL2多模态大模型的自动化核验与差异归因框架。全文610页、61章&#x…

2026/9/30 12:08:02

整车性能目标书(QR)完全指南:模板、分解与版本管理

我在主机厂调试了十来年整车性能,接手新项目的第一件事永远是同一件:翻开上一代车型的性能目标书,和动力总成、底盘、NVH、总布置几个team的人一起过一遍。很多新同事不理解,项目启动阶段最要紧的是节点计划,是零件定点…

2026/9/29 11:07:23

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/29 21:48:03

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/29 7:00:49

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/30 0:01:22

MATLAB+Yalmip+CPLEX实战:综合能源系统优化调度全流程解析

做综合能源系统优化调度这活儿,最痛苦的不是建模本身,而是模型写完之后不知道该怎么求解。看论文里轻飘飘一句“采用Yalmip调用CPLEX求解”,自己上手时却往往卡在环境配置、变量声明、约束写法和求解状态判读上,一耗就是两三天。这…

2026/9/30 0:01:22

I3C比I2C快10倍?RK3576实战:速率、DTS配置与混合总线避坑指南

I3C 比 I2C 快 10 倍?这句话在嵌入式群里传了很久,每次都能吵出一堆截图。前段时间我正好在 RK3576 上调板级 I3C 接口,从控制器寄存器一路摸到 Linux DTS 配置,踩了不少坑,也把这笔速度账彻底算明白了。本文就用 RK35…

2026/9/30 0:01:22

字符串转对象:JSON.parse、new Function与URLSearchParams

“字符串转对象”这几个字,我在技术群里见过的问法至少有十几种:有人拿着一串{a:1,b:2}说 JSON.parse 直接报错,有人要从 URL 里抠出参数,还有人只是想把abc变成能挂属性的东西。js 这门语言里,字符串和对象之间的转换…

2026/9/29 3:53:39

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/29 9:46:12

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/30 10:28:53

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑