<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>迁移 - tag - 沐木</title><link>https://oldletter.cn/tags/%E8%BF%81%E7%A7%BB/</link><description>迁移 - tag - 沐木</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sat, 11 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://oldletter.cn/tags/%E8%BF%81%E7%A7%BB/" rel="self" type="application/rss+xml"/><item><title>工程正确性（三）：迁移登记、Checksum 与回滚边界</title><link>https://oldletter.cn/posts/engineering-correctness-03-database-migrations/</link><pubDate>Sat, 11 Jul 2026 00:00:00 +0000</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/engineering-correctness-03-database-migrations/</guid><description><![CDATA[<blockquote>
<p>系列导航：<a href="/posts/engineering-correctness-01-green-tests-can-be-wrong/" rel="">11</a> | <a href="/posts/engineering-correctness-02-crypto-interoperability/" rel="">12</a> | <a href="/posts/engineering-correctness-03-database-migrations/" rel="">13</a> | <a href="/posts/engineering-correctness-04-coordinate-precision/" rel="">14</a></p></blockquote>
<p>迁移成功不等于数据库已经可用。空库安装验证的是完整历史，线上升级还要考虑旧应用并存、DDL 锁、回填速度和权限。一次服务发布曾在代码和容器准备完成后，生产账号执行迁移却因缺少 <code>ALTER</code> 和 <code>INDEX</code> 权限失败。这个问题不在 SQL 内容，属于迁移前置条件没有进入发布检查。</p>
<h2 id="登记表是事实来源">登记表是事实来源</h2>
<p>迁移记录至少包含版本、文件名、checksum、执行序号、状态、执行人、耗时和错误摘要。已执行文件禁止原地修改，修正使用新版本。启动时发现文件 checksum 与登记值不一致，应停止自动迁移并报警，不能为了让部署继续而覆盖登记表。</p>
<div class="mermaid" id="id-2" data-mermaid-definition="Zmxvd2NoYXJ0IFRECiAgICBzdWJncmFwaCBSZWdpc3RlclvnmbvorrBdCiAgICAgICAgZGlyZWN0aW9uIExSCiAgICAgICAgTVvov4Hnp7vmlofku7ZdIC0tPiBIYXNoW&#43;iuoeeulyBjaGVja3N1bV0gLS0&#43;IFJlZ2lzdHJ5W&#43;i/geenu&#43;eZu&#43;iusOihqF0KICAgIGVuZAogICAgc3ViZ3JhcGggTWlncmF0ZVvlhbzlrrnov4Hnp7tdCiAgICAgICAgZGlyZWN0aW9uIExSCiAgICAgICAgRXhwYW5kW0V4cGFuZCDlhbzlrrnnu5PmnoRdIC0tPiBCYWNrZmlsbFvmuLjmoIflm57loatdIC0tPiBSZWNvbmNpbGVb5YWo6YeP5a&#43;56LSmXQogICAgZW5kCiAgICBzdWJncmFwaCBTaHJpbmtb5YiH5o2i5LiO5pS257ypXQogICAgICAgIGRpcmVjdGlvbiBMUgogICAgICAgIFN3aXRjaFvor7vlhpnliIfmjaJdIC0tPiBDb250cmFjdFtDb250cmFjdCDliKDpmaTml6fnu5PmnoRdCiAgICBlbmQKICAgIFJlZ2lzdHJ5IC0tPiBFeHBhbmQKICAgIFJlY29uY2lsZSAtLT4gU3dpdGNoCiAgICBSZWNvbmNpbGUgLS0&#43;fOi2hemZkHwgUGF1c2Vb5pqC5YGc5LiO6KGl5YG/XQ==">flowchart TD
    subgraph Register[登记]
        direction LR
        M[迁移文件] --&gt; Hash[计算 checksum] --&gt; Registry[迁移登记表]
    end
    subgraph Migrate[兼容迁移]
        direction LR
        Expand[Expand 兼容结构] --&gt; Backfill[游标回填] --&gt; Reconcile[全量对账]
    end
    subgraph Shrink[切换与收缩]
        direction LR
        Switch[读写切换] --&gt; Contract[Contract 删除旧结构]
    end
    Registry --&gt; Expand
    Reconcile --&gt; Switch
    Reconcile --&gt;|超限| Pause[暂停与补偿]</div><p>登记表自身也要受保护。应用账号只读，迁移执行器拥有写入权限。手工 SQL 如果改变了生产结构，要补成正式迁移或登记为已应用版本，不能让测试环境和线上环境依靠“历史操作”维持一致。</p>
<h2 id="expandbackfillcontract">Expand、Backfill、Contract</h2>
<p>Expand 先添加可空字段、兼容索引或快照表，旧应用继续可用。应用升级后双写旧字段和新字段，后台使用稳定主键游标分批回填历史数据。对账完成后切换读路径，观察窗口结束，所有旧实例和离线任务退出，才进入 Contract。</p>
<p>回填条件带上新字段为空、源版本未变化等保护，防止覆盖在线写入。每批记录起止主键、扫描数、更新数、跳过数、失败数和校验摘要。暂停后从已提交游标继续，批次重试具备幂等条件，不能依赖 offset 扫描变化中的数据。</p>
<p>对账不只比较行数。字段拆分检查空值、范围、唯一性和重组结果，表迁移检查关联完整性，编码转换检查不可映射值。允许差异和处理结果写进发布记录，抽样人工核对只作为补充。</p>
<h2 id="回滚按阶段定义">回滚按阶段定义</h2>
<p>增加字段通常可以通过应用开关回退，读切换也可以切回旧路径。已经删除列或完成不可逆数据修正时，回滚依赖备份、归档表或补偿迁移，不能承诺“一键恢复”。发布单在执行前写清每个阶段的恢复动作、数据保留时间和负责人。</p>]]></description></item></channel></rss>