Schema 迁移与 Expand-Contract

共 47 题
📑 题目列表 47 题
#
★★★

1. MySQL InnoDB Online DDL 加列,ALGORITHM=INSTANT、INPLACE?

MySQL InnoDB 在线加列时 ALGORITHM=INSTANT 与 INPLACE 有何区别?

  • INSTANT 与 INPLACE 算法的含义
  • 加列的元数据操作 vs 表重建
  • 适用条件与限制

MySQL 8.0 加列支持 ALGORITHM=INSTANT 与 ALGORITHM=INPLACE。INSTANT 只修改元数据,不重建表、不拷贝数据、不锁表,几乎瞬时完成,是加列的最优选择;但只支持"在表末尾加列"(除非 8.0.29+ 支持在任意位置加),且列数不能超过限制。INPLACE 是"原地修改",通常在表末尾加列时也无需重建数据,在线操作允许并发 DML,但可能重建索引或需要额外空间。两者都支持 Online DDL(不阻塞业务读写),但 INSTANT 更快。若加列会改变数据行长度导致需要重建,则退化为 COPY 或全表重建。

加列优化的关键是"是否重写数据"。INSTANT 只改元数据,INPLACE 可能重建部分结构,COPY 则全表拷贝。选择算法要在满足语义前提下尽量用 INSTANT/INPLACE 避免阻塞。

ALTER TABLE t ADD COLUMN c INT;
-- 显式指定算法
ALTER TABLE t ADD COLUMN c INT, ALGORITHM=INSTANT;
ALTER TABLE t ADD COLUMN c INT, ALGORITHM=INPLACE;
#
★★★

2. PostgreSQL 11+ 加列的优化,ALTER TABLE ADD COLUMN 默认值不重写表?

PostgreSQL 11+ 加列(带默认值)为什么不重写表?其优化机制是什么?

  • PostgreSQL 加列的元数据操作
  • 默认值存储方式的优化
  • 与旧版本的差异

PostgreSQL 中 ALTER TABLE ADD COLUMN 本质是元数据操作,不重写已有数据行(旧行仅在其 page 中标记缺失该列)。PostgreSQL 11 之前的版本,若添加带非常量默认值的列,需要重写整个表;PostgreSQL 11 起,只要默认值是常量或稳定函数(如 DEFAULT 0DEFAULT now()),就把默认值作为"元数据"存储,读取时对缺失的实际值返回该默认值,无需重写表,因此加列瞬时完成且不阻塞。这也使得在线加带默认值的列代价极低。非常量默认值(如 DEFAULT random()DEFAULT clock_timestamp() 这类 volatile 函数)仍需重写表。

关键优化是"把常量默认值下沉为元数据,按需补齐",避免全表重写。这是 PostgreSQL 加列零停机的关键。注意:加列后旧行占用的存储空间仍保持原样,仅实时返回默认值。

-- PG 11+ 常量/稳定默认值(含 now()):元数据操作,不重写表
ALTER TABLE t ADD COLUMN c INT DEFAULT 0;
ALTER TABLE t ADD COLUMN created_at TIMESTAMP DEFAULT now();
-- volatile 默认值(如 random())会触发表重写
ALTER TABLE t ADD COLUMN c2 REAL DEFAULT random();
#
★★★

3. 加列的默认值(DEFAULT)设置,DEFAULT 与 NOT NULL 的协同?

加列时 DEFAULT 与 NOT NULL 如何协同?为什么加 NOT NULL 列注意默认值?

  • NOT NULL 与 DEFAULT 的关系
  • 在线加 NOT NULL 列的实现
  • 数据迁移场景的注意事项

加列时 DEFAULT 与 NOT NULL 协同保证"已有行也能满足 NOT NULL 约束"。若给已有数据的表加 NOT NULL 列,必须先给列提供默认值,否则已有行该列为 NULL 违反约束。MySQL 中 ADD COLUMN c INT NOT NULL DEFAULT 0 在 8.0 用 INSTANT/INPLACE 可快速完成;PostgreSQL 中 ADD COLUMN c INT NOT NULL DEFAULT 0 常量默认值也不重写表。当同一列既要 NOT NULL 又要默认值时,DEFAULT 提供"已有行的填充值",NOT NULL 约束"新行不允许缺省"。在生产中,加 NOT NULL + DEFAULT 可在不重写表的情况下完成,是零停机迁移的常见做法。

NOT NULL 管"约束",DEFAULT 管"缺省值"。加 NOT NULL 列时 DEFAULT 是给存量行兜底的必要条件,二者协同才能在不重写表的前提下完成约束变更。

-- MySQL 8.0 INSTANT:NOT NULL + DEFAULT 常量,不重建表
ALTER TABLE t ADD COLUMN status TINYINT NOT NULL DEFAULT 0;
-- PostgreSQL 11+:常量默认值不重写表
ALTER TABLE t ADD COLUMN status SMALLINT NOT NULL DEFAULT 0;
#
★★★

4. MySQL Online DDL 加列?

MySQL Online DDL 加列是如何实现的?有哪些算法与锁策略?

  • INSTANT/INPLACE/COPY 算法
  • 加列期间的并发 DML
  • 锁策略与元数据锁

MySQL Online DDL 加列通过 ALGORITHM 与 LOCK 两个维度控制。算法:INSTANT(仅改元数据,最快)、INPLACE(原地修改,允许并发 DML)、COPY(全表拷贝,会阻塞)。5.6 起支持 INPLACE 加列(允许并发 DML),8.0 进一步支持 INSTANT。加列过程需要持有一个短暂的元数据锁(MDL)来切换表结构,期间 DML 会被短暂阻塞,但整体可在线执行。选择算法时可用 ALGORITHM=INSTANT, LOCK=NONE 显式指定。若加列改变行格式导致无法原地,则自动退化为 COPY。Online DDL 仍有成本:INPLACE 可能重建索引、占用额外空间与复制延迟。

Online DDL 的核心是"算法(是否重建结构)+ 锁(是否允许并发 DML)"。加列优先 INSTANT/INPLACE,避免 COPY 全表重建带来阻塞。理解该机制是安全做 Schema 迁移的基础。

ALTER TABLE t ADD COLUMN c INT, ALGORITHM=INSTANT, LOCK=NONE;
ALTER TABLE t ADD COLUMN c INT, ALGORITHM=INPLACE, LOCK=NONE;
#
★★★

5. 在线建索引的失败回滚,CONCURRENTLY 的 REINDEX?

PostgreSQL 的 CONCURRENTLY 建索引与 REINDEX 如何实现失败回滚?

  • CREATE INDEX CONCURRENTLY 的机制
  • REINDEX CONCURRENTLY 的用法
  • 失败时的残留索引处理

PostgreSQL 用 CREATE INDEX CONCURRENTLY 在线建索引,不阻塞表的读写,但分多个阶段执行:先建索引对象,再扫描数据构建,最后校验并置为可用。若构建中途失败,会留下一个"无效索引"(invalid index),需要 DROP 后重建。REINDEX CONCURRENTLY 用于在线重建已有索引,同样分阶段,构建期间会创建新索引,成功后原子替换旧索引;失败时同样留下无效索引,需清理。因此 CONCURRENTLY 的失败回滚是"显式清理无效索引 + 重新执行",数据库不会自动回滚到干净状态,需要运维 DROP INDEX 后重试。

CONCURRENTLY 的"失败回滚"是运维层面的清理而非自动回滚。它通过多阶段构建避免阻塞,但失败会残留无效索引,运维需 DROP 后重试。这是在线建索引与普通建索引的根本差异。

CREATE INDEX CONCURRENTLY idx_t_c ON t(c);
REINDEX INDEX CONCURRENTLY idx_t_c;
-- 失败后清理无效索引
DROP INDEX CONCURRENTLY IF EXISTS idx_t_c;
#
★★★

6. 在线建索引的代价,构建时间、I/O 消耗?

在线建索引的代价是什么?构建时间与 I/O 消耗如何评估?

  • 构建时间与数据量、索引复杂度
  • I/O 与 CPU 消耗
  • 对主从复制的影响

在线建索引的代价主要包括:构建时间与数据量、索引大小、机器性能相关;I/O 消耗(读取全表数据、写入索引页)、CPU 消耗(排序、构建 B+ 树)、临时磁盘空间(排序)、以及主从复制延迟(从库也要重建索引,尤其在 MySQL 主库 Online DDL 会同步到从库)。在大表上建索引,即使在线,也会显著消耗 I/O 与 CPU,可能影响在线业务,并可能造成复制延迟。因此要评估:表大小、索引访问模式、业务低峰、复制延迟容忍度。必要时使用限速(如 pt-online-schema-change 的限流)或错峰执行。

在线建索引的"在线"指不阻塞读写,但仍有资源消耗与复制延迟代价。评估核心是"构建时间"与"I/O 峰值",需结合表大小与业务容忍度决定是否在线、何时执行。

#
★★★

7. PostgreSQL 中类型变更的 USING 子句?

PostgreSQL 中类型变更的 USING 子句是什么?如何使用?

  • ALTER COLUMN TYPE 的 USING 表达式
  • 类型转换的显式定义
  • 隐式转换的局限

PostgreSQL 中 ALTER COLUMN TYPE 改变列类型时,若源类型与目标类型之间没有隐式转换,必须用 USING 子句指定如何把旧值转换为新值。USING 可以是任意表达式,例如把字符串列转整数可用 USING col::int,把时间戳转日期可用 USING col::date,也可以用更复杂的表达式(如结合其他列)。这避免了类型转换的歧义,让用户显式控制转换逻辑。注意:类型变更通常需要重写表(除非新旧类型二进制兼容),会锁表并消耗资源,USING 只是解决"如何转换值"。

USING 子句是 PG 类型变更的"转换规则"显式声明。它解决了隐式转换无法覆盖的转换场景,但类型变更本身仍可能重写表、阻塞业务,需谨慎评估。

ALTER TABLE t ALTER COLUMN c TYPE INTEGER USING c::int;
ALTER TABLE t ALTER COLUMN ts TYPE DATE USING ts::date;
#
★★★

8. 列类型变更(ALTER COLUMN TYPE)的代价,表重写、锁等待?

列类型变更(ALTER COLUMN TYPE)的代价是什么?表重写与锁等待如何评估?

  • 类型变更触发表重写
  • 锁的持有与阻塞
  • 在线变更的权衡

PostgreSQL 中多数列类型变更需要重写整个表(把每一行重新写出为新类型),是代价最高的操作之一:全表重写耗时、占用额外磁盘空间、期间持有 ACCESS EXCLUSIVE 锁阻塞对该表的读写。MySQL 中 ALTER COLUMN TYPE 也常触发全表重建(COPY),同样阻塞。若新旧类型二进制兼容(如 varchar 加长、int 升 bigint 的部分情况),可避免重写。因此生产环境变更列类型要评估:表大小、变更窗口、业务可停写时间、是否可用增加新列 + 回填 + 切换的方式(shadow 列)替代。优先选择低峰窗口或影子列双写方案。

类型变更的重头是"表重写 + 阻塞锁"。评估代价就是评估"重写时间"与"锁等待/阻塞窗口"。大表类型变更应避免直接用 ALTER,改用新建列 + 回填 + 切换的渐进式方案。

-- 类型变更,通常触发全表重写
ALTER TABLE t ALTER COLUMN c TYPE BIGINT;
-- 二进制兼容的类型变更不重写(如 varchar 加长)
ALTER TABLE t ALTER COLUMN c TYPE VARCHAR(100);
#
★★★

9. 回填(Backfill)的实现,分批 UPDATE、影子列?

回填(Backfill)如何实现?分批 UPDATE 与影子列方案各有什么特点?

  • 分批 UPDATE 回填
  • 影子列(shadow column)渐进回填
  • 回填的并发与一致性

回填(Backfill)用于给已有数据填充新列的值。常见实现:分批 UPDATE,按主键范围分批执行 UPDATE t SET new_col=... WHERE id BETWEEN ? AND ?,避免一次性更新全表锁表、占用大量资源,每批提交一个小事务,便于断点续跑;影子列方案是新增一个"shadow"列,用应用层双写(新数据写新列,旧数据读取时回填),后台分批回填旧数据,回填完成后切换读取新列并删除旧列。分批 UPDATE 简单但其间读写不一致,可用于可接受短暂不一致的场景;影子列双写更平滑,适合需要渐进、零停顿的场景。

回填的本质是"把存量数据迁移到新结构"。分批 UPDATE 控制事务粒度与资源占用,影子列提供渐进一致的平滑切换。选型取决于停机窗口与一致性要求。

-- 分批回填:按主键逐批更新
UPDATE t SET new_col = compute(old_col)
WHERE id BETWEEN :start AND :end;
-- 循环推进 :start 到下一个批次
#
★★★

10. MySQL 大版本升级(mysql_upgrade)的工具链?

MySQL 大版本升级的工具链是什么?mysql_upgrade 的作用是什么?

  • mysql_upgrade 的作用
  • MySQL 8.0 中升级的机制变化
  • 升级工具链

MySQL 大版本升级依赖一系列工具。mysql_upgrade 用于升级系统表(如 mysql 库中的权限表、帮助表)并检查与修复表以匹配新版本,传统上需要在升级后运行。MySQL 8.0 起,mysql_upgrade 已整合进 mysqld 启动流程(--upgrade 选项),升级时由服务器自动检测并升级系统表,命令行 mysql_upgrade 被废弃但仍可用。完整升级工具链还包括:备份(mysqldump/xtrabackup)、版本检查(mysqlcheck)、参数比对(mysql_config_editor、mysqldiff)、以及升级后验证。升级前必须备份并做兼容性检查(如旧 SQL 语法、系统表格式)。

mysql_upgrade 的核心是"升级系统表并校验数据字典"。8.0 自动升级改变了操作方式,但"升级前备份 + 升级后验证"的原则不变。工具链价值在于把升级流程标准化、可恢复。

#
★★★

11. PostgreSQL 大版本升级(pg_upgrade)的实现,原地升级 vs 逻辑复制?

PostgreSQL 大版本升级如何实现?pg_upgrade 原地升级与逻辑复制升级有何区别?

  • pg_upgrade 的原地链接升级
  • 逻辑复制升级
  • 两种方式的取舍

PostgreSQL 大版本升级主要两种方式:pg_upgrade 原地升级,通过"链接模式"(link mode)把旧数据文件硬链接到新集群,避免重新拷贝数据,升级快、停机时间短(通常分钟级),但需要新旧版本二进制兼容、需停机维护新集群;逻辑复制(logical replication)升级,先建新版本集群,用逻辑复制把数据从旧集群同步到新集群,切换时停机时间极短(仅切换写),适合追求最小停机的场景,但设置复杂、需要处理复制对象的差异。二者取舍:pg_upgrade 简单直接、停机分钟级;逻辑复制停机更短但复杂度高。大型库通常用 pg_upgrade 或逻辑复制组合(如先逻辑复制再切流量)。

pg_upgrade 是"原地升级",逻辑复制是"并行迁移"。原地升级停机短但需停机,逻辑复制可近乎零停机但复杂。选型权衡停机窗口、数据结构与运维能力。

#
★★★

12. ORM 自动生成 DDL 与手写 DDL 的对比?

ORM 自动生成 DDL 与手写 DDL 有何区别?各自的优缺点?

  • ORM 生成的便捷性与自动性
  • 手写 DDL 的可控性与性能
  • 混合策略

ORM(如 Hibernate、JPA、GORM)能根据实体类自动生成 DDL(建表、加列、加索引),开发快捷、模型与代码同步,适合快速迭代;但 ORM 生成 DDL 通常是"开发环境可用、生产不可控",无法精细控制索引、分区、引擎、Online DDL 策略,且自动变更可能产生破坏性操作(如删列、类型变更)。手写 DDL 配合迁移工具(Flyway/Liquibase)可精确控制每步变更、可评审、可回滚、可灰度,适合生产。实践中常用"ORM 生成开发表 + 手写迁移脚本管理生产 Schema",用 Flyway 等工具管理增量变更。

ORM 自动 DDL 侧重"快速同步模型",手写 DDL 侧重"生产可控"。生产环境几乎必须手写迁移脚本,因为 ORM 无法满足在线变更、索引优化、回滚等运维需求。

#
★★★

13. 影子流量的实现,TCP 复制、CDC、应用层双写?

影子流量(Shadow Traffic)如何实现?TCP 复制、CDC、应用层双写各有什么特点?

  • TCP 复制影子流量
  • CDC(Change Data Capture)影子
  • 应用层双写

影子流量(Shadow Traffic)把生产流量复制到新版本/新数据库用于验证,而不影响真实业务。实现方式:TCP 复制方式在七层/四层代理层把请求复制一份(如 TCP 镜像、流量镜像)到影子环境,用于验证新版本但会放大流量;CDC 方式通过解析生产库的 binlog/WAL 把增量变更同步到影子库,验证新库的存储与查询;应用层双写则是应用把写操作同时写入新旧两套存储,读取对比结果,用于验证数据一致性。三种方式各有适用:TCP 复制造价高但最真实,CDC 适合验证数据同步,应用层双写适合验证应用逻辑与数据一致性。影子流量常用于大版本升级、新库迁移、代码重构的回归验证。

影子流量的本质是"隔离验证 + 流量复制"。它把真实流量引导到新环境,验证新系统正确性而不影响线上。选型取决于验证目标(版本、数据、逻辑)与成本。

#
★★★

14. 影子流量(Shadow Traffic)的概念,将生产流量复制到新版本/新数据库?

影子流量(Shadow Traffic)的概念是什么?如何将生产流量复制到新版本/新数据库用于验证?

  • 影子流量的定义
  • 复制生产流量的方式
  • 验证目的与隔离性

影子流量(Shadow Traffic)指把生产环境的真实流量复制一份到新版本或新数据库,用于验证新系统在真实负载下的行为,而不影响生产。它保证影子环境与生产逻辑隔离:影子流量处理结果不影响生产,也不对生产产生副作用。复制方式包括:代理层流量镜像(把请求复制到影子)、数据库层 CDC(把 binlog/WAL 增量同步到影子库)、应用层双写(同时写新旧库对比)。验证内容:新版本的正确性、性能、数据一致性、兼容性。影子流量是新数据库迁移、大版本升级、架构重构前的关键验证手段。

影子流量的核心价值是"用真实流量验证新系统,同时隔离副作用"。它把"上线后发现问题的风险"前置到验证阶段。是否隔离、是否真实、是否可对账是评价影子流量方案的关键。

#
★★★

15. pt-online-schema-change 的工作原理(创建影子表 → 在原表建 INSERT/UPDATE/DELETE 三个触发器同步增量 → 分批拷贝存量 → RENAME 互换)是什么?触发器带来哪些额外写放大与风险?

pt-online-schema-change 的工作原理是什么?触发器带来哪些额外写放大与风险?

  • 创建影子表 + 触发器同步增量
  • 分批拷贝存量数据
  • RENAME 切换

pt-online-schema-change(pt-osc)的工作流程:1) 创建一份与原表结构一致的影子表(ghost table),在上面执行目标 DDL;2) 在原表上创建 INSERT/UPDATE/DELETE 三个触发器,把原表的增量写操作实时同步到影子表;3) 分批拷贝存量数据到影子表(通过 chunk 分批,配合限速);4) 拷贝完成后,用 RENAME 把原表与影子表原子互换(通过 RENAME TABLE 或反向 RENAME 加清理),随后删除触发器与旧表。由于触发器对每个原表写操作额外执行一次影子表写,带来了显著的写放大(每行写操作在影子表再执行一次),增加 I/O 与锁竞争,且触发器与原表已有触发器可能冲突,开发时可暂停。此外 DDL 期间若触发器大量执行,可能拖慢原表写入。

pt-osc 用"触发器 + 分批拷贝 + RENAME 切换"实现在线改表,但触发器的写放大与锁竞争是主要代价,且要求原表有主键或唯一键用于分批与去重。风险还包括触发器冲突、复制延迟、大事务。

#
★★★

16. gh-ost 如何通过解析 binlog(而非触发器)把增量应用到 ghost 表?cut-over 阶段借助辅助表与 RENAME 原子切换的关键步骤是什么?

gh-ost 如何通过解析 binlog 把增量应用到 ghost 表?cut-over 阶段借助辅助表与 RENAME 原子切换的关键步骤是什么?

  • gh-ost 解析 binlog 应用增量
  • 辅助表(cut-over 表)的作用
  • RENAME 原子切换

gh-ost 与 pt-osc 的本质区别是:gh-ost 不创建触发器,而是作为 MySQL 的复制从属(replica)解析主库的 binlog,把增量变更(INSERT/UPDATE/DELETE)应用到 ghost 表。它的工作流:gh-ost 建立到主库的连接,先对原表做一次性拷贝建 ghost 表并执行目标 DDL,然后开启 binlog 监听,把拷贝期间产生的增量事件经 binlog 应用线程同步到 ghost 表,同时后台分批拷贝存量数据。cut-over 阶段:gh-ost 创建一张辅助表(cut-over 表)作为"切换锁",通过原子操作把原表 RENAME 到辅助表、把 ghost 表 RENAME 为原表名,这一对 RENAME 借助 MySQL 的原子 DDL 与锁实现近乎瞬时的切换,期间短暂阻塞写入。切换后清理辅助表与旧表。

gh-ost 用"binlog 增量 + 分批拷贝 + 原子 RENAME"实现无触发器在线改表,消除了触发器写放大。cut-over 的原子切换是零停机关键,辅助表用于协调切换时序避免丢数据。

#
★★★

17. gh-ost 如何通过 --max-load、--critical-load、--max-lag-millis 限流,并通过 Unix socket 命令(throttle/pause/cutover/cancel)做运行期控制?

gh-ost 如何通过 --max-load、--critical-load、--max-lag-millis 限流?如何用 Unix socket 命令做运行期控制?

  • 限流参数(max-load、critical-load、max-lag-millis)
  • 运行期控制命令
  • 主从延迟保护

gh-ost 提供多种限流机制保护在线业务。--max-load 指定当主库的负载指标(如 Threads_running、Threads_connected)超过阈值时暂停拷贝;--critical-load 指定更严格的阈值,超过后 gh-ost 直接中止(fatal),避免拖垮主库;--max-lag-millis 指定允许的最大复制延迟,若从库延迟超过该值,gh-ost 暂停应用 binlog 增量,避免进一步放大从库延迟。运行期控制通过 Unix socket 命令:gh-ost 启动后监听一个 socket 文件,运维可用 echo throttle | socat - /tmp/ghost.sock 等命令发送 throttle(暂停)、pause(完全暂停)、cutover(触发切换)、cancel(取消)等指令,实现不改 config 的动态控制。这些机制让在线改表可随时"降速、暂停、切换、取消"。

gh-ost 的限流是"自动限流 + 人工控制"结合:max-load/critical-load 基于主库负载,max-lag 基于从库延迟,socket 命令提供人工干预。这让在线改表在真实负载波动下保持安全。

#
★★★

18. 数据库迁移与应用发布的顺序(migration first vs app first)与 Expand-Contract 的落地

数据库迁移与应用发布的顺序(migration first vs app first)如何选择?与 Expand-Contract 如何落地?

  • migration first 与 app first 的区别
  • 向前兼容与向后兼容
  • Expand-Contract 的三阶段落地

数据库迁移与应用发布的顺序决定兼容性。migration first(先迁移库后发布应用):先加新列/新表,应用发布后再读新字段,保证新应用兼容旧数据;app first(先发布应用后迁移库):应用先写新字段,但此时库还没有该列会失败,通常不推荐。实际上 Expand-Contract 模式是标准做法:Expand(先加列/表,不删除旧结构,应用层同时兼容新旧)→ Contract(应用已全部使用新结构后,再删除旧列/旧表)。这样迁移与发布解耦,任何一步失败都可回滚,无需停机。migration first 对应 Expand 阶段,Contract 阶段对应清理旧结构,两者结合实现零停机、可回滚的 Schema 演进。

顺序的核心是"兼容性"。Expand-Contract 保证"先加后删、应用可前可后",任何一步失败都可回滚。这是数据库迁移与发布协同的黄金法则。

#
★★

19. pt-osc 与 gh-ost 如何选型,触发器开销与已有触发器冲突、外键处理(alter-foreign-keys-method)、主从延迟控制、可暂停性方面的差异?

pt-osc 与 gh-ost 如何选型?在触发器开销、外键处理、主从延迟控制、可暂停性方面有何差异?

  • 触发器 vs binlog 增量
  • 外键处理方式
  • 主从延迟控制与可暂停性

pt-osc 与 gh-ost 选型差异主要围绕实现机制。触发器开销:pt-osc 用触发器同步增量,每行写造成写放大,且若原表已有触发器会冲突;gh-ost 解析 binlog 无触发器,无写放大、无触发器冲突。外键处理:pt-osc 用 --alter-foreign-keys-method 指定外键处理(如 rebuild/auto 重建外键),gh-ost 较难处理外键(需外键约束的列相关)。主从延迟控制:两者都支持检查从库延迟,gh-ost 提供 --max-lag-millis 自动暂停,pt-osc 用 --check-slave-lag。可暂停性:gh-ost 通过 socket 命令可随时 throttle/pause/cutover/cancel,pt-osc 的暂停控制较弱。选型:gh-ost 更现代、无触发器、控制力强,适合大表;pt-osc 成熟、外键处理更完善,适合有外键且需久经考验的场景。

选型核心是"触发器 vs binlog、外键支持、暂停控制"。gh-ost 无触发器写放大、控制更好,但外键不如 pt-osc 成熟。有外键的表慎用 gh-ost,追求精细控制选 gh-ost。

#
★★

20. gh-ost/pt-osc 的行拷贝批次(chunk-size/rows-per-chunk)如何控制?为什么要求表上有唯一键/主键才能安全分批?

gh-ost/pt-osc 的行拷贝批次如何控制?为什么要求表上有唯一键/主键才能安全分批?

  • chunk-size/rows-per-chunk 控制
  • 分批拷贝的定位
  • 唯一键/主键的必要性

gh-ost/pt-osc 分批拷贝存量数据,批次大小由 chunk-size(pt-osc 的 rows-per-chunk / gh-ost 的 chunk-size,默认约 1000 行)控制,每批用范围查询限定位拷贝,批间可限速、可暂停。分批拷贝要求表上有唯一键或主键,因为分批定位需要"按主键范围切分"(如 WHERE id > last_id AND id <= last_id + chunk),且增量与存量并发时要用唯一键去重(同一行可能被存量拷贝和 binlog 增量各写一次,需避免重复)。若无唯一键,无法确定"切分边界"和"该行是否已拷贝",可能导致重复或遗漏。这也是在线改表工具要求"表必须有主键/唯一键"的原因。

分批拷贝把改表拆成可控的小事务,利于限速与断点;唯一键/主键提供"切分锚点"与"去重依据"。无唯一键则无法安全分批,是工具的前提条件。

#
★★

21. 哪些 DDL 不适合用在线改表工具(重命名被触发器引用的列、修改主键定义、全文/空间索引)?各自的风险点是什么?

哪些 DDL 不适合用在线改表工具?重命名被触发器引用的列、修改主键定义、全文/空间索引的风险点是什么?

  • 重命名被触发器引用的列
  • 修改主键定义
  • 全文/空间索引

在线改表工具(pt-osc/gh-ost)不适合某些 DDL。重命名被触发器引用的列:pt-osc 的触发器逻辑依赖列名,重命名列会导致触发器失效或错误,gh-ost 也可能因 binlog 事件列名不一致出错;修改主键定义:在线工具依赖主键分批拷贝与切分,修改主键会破坏分批定位与去重,导致工具无法安全运行;全文/空间索引:这类索引的构建与增量同步复杂,在线工具无法正确维护,且 MySQL 对全文/空间索引的 DDL 限制多。这些 DDL 的风险点在于"工具的分批/增量同步机制会失效",需改用原生 DDL 或停机维护。

在线工具的核心前提是"主键可分片 + 增量可同步"。破坏这些前提的 DDL(改列名、改主键、特殊索引)不适合在线工具,需评估停机窗口或原生 Online DDL。

#
★★

22. MySQL 8.0 INSTANT DDL 覆盖加列等操作后,第三方在线改表工具仍不可替代的场景有哪些(列类型变更、大表改字符集、重建聚簇索引)?

MySQL 8.0 INSTANT DDL 覆盖加列后,第三方在线改表工具仍不可替代的场景有哪些?

  • INSTANT DDL 的覆盖范围
  • 列类型变更、改字符集、重建聚簇索引
  • 第三方工具的适用场景

MySQL 8.0 的 INSTANT 算法覆盖了加列等简单操作,但很多 DDL 仍无法 INSTANT,第三方在线改表工具(pt-osc/gh-ost)仍不可替代。典型场景:列类型变更(ALTER COLUMN TYPE 多数需全表重建,INSTANT 不适用);大表改字符集(如 utf8mb3→utf8mb4,需全表重写,INSTANT 无法覆盖);重建聚簇索引(修改主键/重建聚簇索引需要重建整表,INSTANT 无能为力);改列在表中间位置(8.0.29 前 INSTANT 不支持)等。这些操作涉及全表数据重写,在线工具通过"分批拷贝 + 增量同步"实现低停机迁移,是 INSTANT 无法替代的。

INSTANT 只覆盖"元数据级"变更,凡涉及"数据重写"的 DDL(类型、字符集、聚簇索引)都必须借助在线工具或停机。理解 INSTANT 边界是判断何时需要第三方工具的关键。

#
★★

23. 在线改表工具如何与主从架构配合(--check-slave-lag 指定延迟检测从库、主从延迟超阈值自动暂停)?

在线改表工具如何与主从架构配合?--check-slave-lag 如何检测从库延迟并自动暂停?

  • --check-slave-lag 指定检测从库
  • 主从延迟超阈值自动暂停
  • 从库资源保护

在线改表工具在主从架构下,需避免改表过程拖垮从库。pt-osc 的 --check-slave-lag 参数指定一个从库用于检测复制延迟,工具定期检查该从库的 Seconds_Behind_Master,若超过阈值(如 --max-lag)则自动暂停拷贝,等从库追平后再恢复,从而保护从库不被改表放大延迟。gh-ost 的 --max-lag-millis 与该机制类似,因为 gh-ost 本身作为复制从属应用 binlog,会监测自身复制延迟。配合主从架构,在线改表还应考虑:从库也要执行 DDL(MySQL 复制从库执行 DDL 的方式)、避免改表时从库读请求漂移、延迟过大的回退策略。

在线改表与主从架构配合的核心是"从库延迟保护"。通过检测从库 Seconds_Behind_Master 并自动暂停,避免改表增量放大复制延迟、拖垮从库。这是大型库在线改表的关键保障。

#
★★

24. 数据库迁移(Migration)的版本管理,Flyway、Liquibase、sqitch?

数据库迁移的版本管理工具有哪些?Flyway、Liquibase、sqitch 各自的特点?

  • Flyway 的 SQL 脚本版本管理
  • Liquibase 的 changelog 抽象
  • sqitch 的依赖图与回滚

数据库迁移版本管理工具把 Schema 变更纳入版本控制。Flyway 用版本化 SQL 脚本(V1__xxx.sql、V2__xxx.sql),按顺序执行并记录到 schema_version 表,简单直接,支持升级与回滚(需 undo 脚本);Liquibase 用 changelog(XML/YAML/SQL)描述变更,支持跨数据库抽象、幂等执行与条件变更,适合复杂企业环境;sqitch 基于变更依赖图(每个变更声明依赖与冲突),支持精细的回滚编排,适合多环境协作与复杂回滚。选型:Flyway 简单适合大多数项目,Liquibase 功能丰富适合企业级,sqitch 适合需要对回滚精细控制的场景。

迁移工具的价值是"把 Schema 变更变为可版本化、可追溯、可执行、可回滚的产物"。Flyway 简单、Liquibase 抽象、sqitch 依赖图化,选型取决于团队协作与回滚复杂度。

#
★★

25. 迁移漂移(Drift)的检测,schema 与代码不一致?

迁移漂移(Drift)是什么?如何检测 schema 与代码不一致?

  • 迁移漂移的定义
  • 漂移检测工具
  • 漂移的预防

迁移漂移(Drift)指生产数据库的 schema 与代码/迁移脚本预期的 schema 不一致,通常由人工直接改库、未执行迁移脚本、环境差异等原因造成。漂移检测通过对比"实际 schema 与期望 schema"实现:用 Schema Diff 工具(如 migra、pgdiff、pt-diff、mysqldiff)对比生产库与迁移脚本构建的基线库,找出差异;或对比生产与测试环境的 schema。检测到漂移后需评估差异影响并修复(走迁移脚本补上或回滚)。预防漂移的方法:禁止人工改库、强制所有变更走迁移工具、CI 中自动校验 schema 一致性、定期执行 diff 巡检。

漂移的本质是"环境之间 schema 不一致"。检测靠 diff 工具,预防靠"变更全走迁移管道 + 定期一致性校验"。漂移会导致代码期望的列不存在、数据类型不符等线上故障。

#
★★

26. 迁移脚本的向前兼容与回滚策略,Expand-Contract 模式?

迁移脚本如何做到向前兼容与可回滚?Expand-Contract 模式如何应用?

  • 向前兼容的定义
  • 回滚策略
  • Expand-Contract 的应用

迁移脚本的向前兼容指"新版本 Schema 能被旧版本应用读取",回滚策略指"失败时能恢复到旧 Schema"。Expand-Contract 模式是标准答案:Expand 阶段只加新列/新表,不删除旧结构,保证旧应用仍能读写(向前兼容);Contract 阶段待所有应用都使用新结构后再删除旧列/表,此时删除不会破坏兼容。回滚时,Expand 阶段回滚只需 DROP 新加的结构(不破坏旧应用),Contract 阶段回滚则需重新加回旧列。这样迁移与发布解耦,任何阶段失败都可安全回滚。避免"一次性改列+删列"导致旧应用无法读取。

向前兼容与可回滚是"先加后删、渐进收敛"的必然结果。Expand-Contract 让每次变更都是"可向前、可回滚"的增量,是零停机迁移的核心。

#
★★

27. 零停机迁移(Zero-Downtime Migration)的实践,影子流量、双写?

零停机迁移(Zero-Downtime Migration)的实践是什么?影子流量与双写如何配合?

  • 零停机迁移的目标
  • 影子流量验证
  • 双写与切换

零停机迁移指在业务不中断的情况下完成数据库迁移(如存量迁移、切库、类型变更)。实践各阶段:先做影子流量验证(把生产流量复制到新库,验证新库正确性与性能);再双写(应用同时写新旧库,保证迁移期间数据一致),期间用对账校验新旧库一致性;双写稳定后做流量切换(读流量灰度切到新库,写流量最终切换);最后停写旧库并清理。关键是"验证→双写→对账→切换→清理"的渐进式流程,配合 Expand-Contract 与反向切换(回滚)能力。零停机迁移需要应用层支持双写与灰度,复杂度高但可避免停机。

零停机迁移的实质是"以渐进切换替代一次性停机"。影子流量验证正确性、双写保证一致性、对账发现偏差、灰度切换控制风险。每步都可回滚是零停机的前提。

#
★★

28. Expand-Contract 与一致性?

Expand-Contract 与一致性是什么关系?为什么它有助于保证一致性?

  • Expand-Contract 三阶段
  • 迁移期间的一致性窗口
  • 降低不一致风险

Expand-Contract 通过"先加后删"的渐进式变更,降低了迁移不一致的风险。Expand 阶段只新增结构,不改变旧读路径,旧应用与新应用都能正常读写,迁移期间数据一致性不受破坏;Contract 阶段在确认所有应用已迁移后才删除旧结构,避免旧应用读到空/不一致数据。相比一次性"改列+删列",Expand-Contract 让每个阶段都保持"新旧结构并存"的中间状态,通过双写/对账保证并存期间数据一致,最后收敛到新结构。它把"迁移的一致性"从"一次性原子切换"转化为"渐进收敛",从而在保证一致性的同时实现零停机。

Expand-Contract 是"一致性 + 可用性"的权衡艺术。它让迁移期间新旧结构并存,通过双写与对账保证并存期一致,最终无痛收敛。没有它,一次性迁移容易在切换窗口产生不一致。

#
★★

29. Contract 阶段,删除旧列、旧表?

Contract 阶段做什么?如何安全删除旧列、旧表?

  • Contract 阶段删除旧结构
  • 删除前的确认
  • 删除的时机与回滚

Contract 阶段是 Expand-Contract 的收尾,在应用已全部使用新结构后,删除旧列、旧表、旧索引等不再使用的结构。删除前必须确认:所有应用实例已发布并停止使用旧结构(通过代码扫描、监控是否有旧列访问)、确认读路径已切换到新列。删除旧列可用 Online DDL(MySQL)或 PG 的 DROP COLUMN,删除旧表需确认无依赖、无外键引用。由于删除是不可逆的(或需重建成本高),Contract 阶段要谨慎,通常在生产稳定一段时间后执行,并保留回滚预案(如临时保留旧表)。Contract 阶段相比 Expand 阶段风险更高,因为删除操作不可轻易回退。

Contract 阶段是"清理旧结构",风险在于删除不可逆。安全的做法是"确认无使用 + 稳定期后删除 + 保留回滚预案"。删除越晚越安全,但旧结构会长期占用资源。

-- Contract 阶段:删除旧列
ALTER TABLE t DROP COLUMN old_col;
-- 删除旧表(先确认无依赖)
DROP TABLE t_old;
#
★★

30. Expand 阶段,加列加表,不删除旧结构?

Expand 阶段做什么?为什么只加列加表而不删除旧结构?

  • Expand 阶段的新增操作
  • 不删除旧结构的原因
  • 新旧并存的兼容性

Expand 阶段是 Expand-Contract 的第一步,只新增结构:加新列、新表、新索引、新约束,不删除任何旧结构。这样做的核心原因是向前兼容:新增结构不破坏旧应用对旧结构的读写,旧应用仍能正常工作,同时新应用可以开始使用新结构。新旧结构并存期间,通过双写/默认值/回填保证数据一致。Expand 阶段不删除旧结构,是因为删除会破坏旧应用兼容性,违背"零停机、可回滚"的目标。Expand 阶段是可逆的(回滚只需 DROP 新加的结构,不影响旧应用),因此风险低。

Expand 阶段"只加不删"保证了向前兼容与可回滚。新增结构是纯增量,旧读路径不受影响,是低风险的一步。它是 Contract 阶段的前提。

-- Expand 阶段:加新列
ALTER TABLE t ADD COLUMN new_col INT;
-- 加新表
CREATE TABLE t_new (...);
#
★★

31. Expand-Contract 与在线 DDL 的协同?

Expand-Contract 与在线 DDL 如何协同?

  • 在线 DDL 支撑 Expand 阶段
  • 在线 DDL 支撑 Contract 阶段
  • 对零停机的影响

Expand-Contract 与在线 DDL 协同实现零停机迁移。Expand 阶段加列/加索引可用在线 DDL(MySQL INSTANT/INPLACE、PG ADD COLUMN 元数据操作)快速完成,不阻塞业务;Contract 阶段删除旧列/旧表也可用在线 DDL 或低峰窗口执行。在线 DDL 让 Expand-Contract 的每一步都不需要停机,从而全程零停机。同时在线 DDL 的算法(INSTANT/INPLACE/COPY)决定了每一步的代价,Expand 阶段优先选 INSTANT/INPLACE 避免重表,Contract 阶段删除列也需要评估重写代价。二者协同:Expand-Contract 定"流程",在线 DDL 提供"具体执行的零停机手段"。

Expand-Contract 解决"何时加删"的编排,在线 DDL 解决"如何不加锁地加删"的机制。两者结合使 Schema 演进全程零停机、可回滚。

#
★★

32. Expand-Contract 模式的三阶段,扩展(Expand)、迁移(Contract)、清理(Cleanup)?

Expand-Contract 模式的三阶段是什么?扩展、迁移、清理各做什么?

  • Expand 扩展阶段
  • Contract 迁移(收敛)阶段
  • Cleanup 清理阶段

Expand-Contract 是三阶段渐进式迁移。第一阶段 Expand(扩展):新增列、表、索引等结构,应用开始兼容新旧,不删除旧结构;第二阶段 Contract(迁移/收敛):应用逐步切换到新结构,读路径从旧列切到新列,数据回填完成,旧结构不再被使用;第三阶段 Cleanup(清理):待所有应用稳定使用新结构后,删除旧列、旧表、旧索引,收敛资源。三阶段强调"先加后删、循序渐进",每阶段都可验证、可回滚,最终无痛完成迁移。相比一次性的"改列+删列",三阶段让不一致风险最小化。

三阶段的核心是"渐进收敛"。Expand 低风险加新,Contract 切换用量,Cleanup 收尾清理。每阶段之间留有验证与回滚窗口,是零停机迁移的思想框架。

#
★★

33. 大表改键的回滚(在迁移与在线 DDL 范畴内)?

大表改键(如改主键)如何回滚?在迁移与在线 DDL 范畴内如何处理?

  • 改键的代价与风险
  • 回滚策略
  • 大表改键的注意事项

大表改键(改变主键/唯一键)是重操作,通常需要重建聚簇索引或全表重写,代价高、时间长。回滚策略:由于改键不可逆且代价高,应在改前保留完整备份与基线,改后用 DDL 逆操作(如改回原键)重建,但同样耗时;更稳妥的做法是用影子表/新表方案:先建新表(含新键),双写/迁移数据,验证通过后用 RENAME 切换,原表保留作为回滚基线。这样回滚只需 RENAME 切回旧表,无需重跑耗时的改键。此外改键期间要评估对在线业务与复制的影响,尽量低峰执行,并有数据校验与对账。

大表改键的回滚核心是"保留可快速回滚的旧结构"。影子表+切换方案比"直接 ALTER 改键"更利于回滚,因为回滚只需切回旧表。改键前备份、改后对账是底线。

#
★★

34. 分区数据的重分布(Rebalance),Online vs Offline?

分区数据的重分布(Rebalance)如何实现?Online 与 Offline 方式有何区别?

  • 分区重分布的目的
  • Online 重分布
  • Offline 重分布

分区数据重分布(Rebalance)指调整分区布局(如重新分区、调整分区键、合并/拆分分区、按新键重排数据),常见于数据增长、分区键变化、负载不均。Offline 重分布:停业务后重建分区表并迁移数据,简单可靠但需停机。Online 重分布:通过在线工具/渐进式迁移,创建新分区表(新布局),双写/分批迁移数据,验证后切换,保持业务连续。Online 方式配合影子流量、对账、灰度切换实现零停机,但复杂度高。选型取决于停机窗口与数据量。分区重分布要特别关注分区键变更对数据分布与查询路由的影响。

Rebalance 的 Online/Offline 与"停机 vs 不停机"的取舍一致。Offline 简单适合小库/可停机,Online 渐进复杂适合大库/零停机。分区键变更影响数据分布,是重分布的核心。

#
★★

35. 分区表的拆分(Detach/Attach)与重组?

分区表的拆分(Detach/Attach)与重组如何实现?

  • Detach/Attach 操作
  • 分区拆分与重组
  • 原子性与一致性

分区表的分区管理常用 DETACH/ATTACH:PostgreSQL 支持 ALTER TABLE ... DETACH PARTITION 把分区从父表分离(可独立处理、迁移、归档),以及 ALTER TABLE ... ATTACH PARTITION 把分区重新挂回父表,两者都尽可能快地完成(元数据操作),适合做分区归档与扩容。MySQL 8.0 支持 ALTER TABLE ... EXCHANGE PARTITION(分区与普通表交换)用于拆分重组。分区重组当分区键变化或数据需要重排时,需创建新分区表、迁移数据、再 ATTACH 或切换。拆分重组要保证分区边界与数据一致,避免数据落入错误分区。DETACH 后分区可独立备份/删除,ATTACH 前需校验分区约束匹配。

DETACH/ATTACH 是分区表"低成本拆分挂接"的机制,适合归档与扩容。重组本质是"建新分区布局 + 迁移数据 + 切换",需保证边界与数据一致。

-- PostgreSQL 分离/挂接分区
ALTER TABLE t DETACH PARTITION t_2024;
ALTER TABLE t ATTACH PARTITION t_2024 FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');
-- MySQL 交换分区
ALTER TABLE t EXCHANGE PARTITION p0 WITH TABLE t_p0;
#
★★

36. 分区表(Partitioned Table)的迁移,从普通表转为分区表?

如何把普通表迁移为分区表?分区表迁移的注意事项?

  • 普通表转分区表的方案
  • 数据迁移与校验
  • 分区键选择

把普通表迁移为分区表,常见方案:先建一张分区表(同结构 + 分区定义),再分批把普通表数据迁移到分区表(INSERT INTO ... SELECT 按分区范围分批),迁移完成后校验行数/数据一致性,最后 RENAME 切换(应用切换表名),并清理旧表。也可用工具(如 pt-archiver、pg_partman)辅助。迁移注意事项:分区键要选对(常按时间或业务范围,保证查询裁剪与数据均衡);迁移期间需处理新写入(双写或停机窗口);迁移后要验证分区裁剪生效;分区定义要覆盖全部数据(避免数据越界失败)。大表迁移应分批、低峰执行,并保留回滚。

普通表转分区表是"建新表 + 迁移 + 切换"的典型迁移。分区键决定未来查询性能与数据分布,是迁移成败的关键。分批迁移控制资源占用,RENAME 实现切换。

CREATE TABLE t_partitioned (...) PARTITION BY RANGE (created_at) (
  PARTITION p2024 VALUES LESS THAN ('2025-01-01'),
  PARTITION p2025 VALUES LESS THAN ('2026-01-01')
);
-- 分批迁移数据
INSERT INTO t_partitioned SELECT * FROM t WHERE created_at < '2025-01-01';
#
★★

37. 跨版本升级的测试策略,影子库、A/B 测试?

跨版本升级的测试策略是什么?影子库与 A/B 测试如何应用?

  • 影子库验证
  • A/B 测试
  • 升级前的回归与容量验证

跨版本升级前需充分测试。影子库:搭建新版本库,把生产数据复制/回放到影子库,跑生产流量验证新版本的功能、性能、兼容性,不污染生产。A/B 测试:把部分真实流量灰度到新版本,对比新旧版本的功能与性能指标(如延迟、错误率、资源占用),逐步扩大灰度比例。此外还需做:功能回归测试(SQL 兼容性、存储过程)、性能压测(TPC-C/自定义负载)、容量验证(新版本数据字典/索引大小)、迁移演练(完整跑一遍升级流程)。测试策略原则是"先用影子库验证,再用 A/B 灰度冒烟,最后全量切换"。

跨版本升级测试的价值是"把风险前置到迁移前"。影子库验证"正确性",A/B 验证"生产环境真实表现",两者结合确保升级可靠。灰度比例控制是 A/B 的核心。

#
★★

38. Schema Diff 工具,migra、pgdiff、apgdiff、mydbforge?

Schema Diff 工具(migra、pgdiff、apgdiff、mydbforge)各有何特点?

  • migra 的 PostgreSQL diff
  • pgdiff/apgdiff 的差异
  • mydbforge 的跨库

Schema Diff 工具对比两个数据库的 schema 差异并生成迁移语句。migra 是 PostgreSQL 专用 diff 工具,可生成新旧 schema 的差异 SQL,支持作为库或命令行使用;pgdiff(apgdiff)也是 PostgreSQL 的 schema diff 工具,生成 DDL 差异脚本;mydbforge(如 dbForge Schema Compare)是商业图形化工具,支持多种数据库(MySQL、SQL Server、Oracle 等)的可视化对比与同步。选型:PostgreSQL 生态常用 migra/apgdiff,跨库或商业运维常用 dbForge 等。这些工具用于迁移漂移检测、环境一致性对比、迁移脚本生成。

Schema Diff 工具的价值是"自动发现 schema 差异并生成迁移脚本"。不同工具覆盖不同数据库生态与交互方式,选型取决于数据库类型与对可视化/自动化的需求。

#
★★

39. 生产与测试环境的 Schema 一致性检测?

生产与测试环境的 Schema 一致性如何检测?

  • 一致性检测的方法
  • 检测工具
  • 一致性维护

生产与测试环境 Schema 一致性检测用于发现测试环境与生产环境的差异(如漏执行迁移、人为改库)。方法:用 Schema Diff 工具对比两库的 object 定义(表、列、索引、约束、视图、函数),并结合迁移脚本历史确认。可定时执行(如 CI 中定期对比)生成差异报告。检测工具:PostgreSQL 用 migra/apgdiff,MySQL 用 mysqldiff、pt-diff 或 information_schema 对比脚本。维护一致性的关键:所有变更走同一套迁移工具(Flyway/Liquibase),两端按相同顺序执行迁移脚本,禁止人工改库,CI 中自动校验。一致性检测是迁移漂移治理的一部分。

一致性检测的本质是"对比实际 schema",核心手段是 diff 工具 + 迁移流程统一。流程统一(变更走同一迁移管道)是预防,diff 是检测,两者结合维护环境一致性。

#
★★

40. Schema 对比的最佳实践(在迁移与在线 DDL 范畴内)?

Schema 对比的最佳实践是什么?在迁移与在线 DDL 范畴内如何应用?

  • 对比的时机与对象
  • 对比工具与自动化
  • 与迁移流程结合

Schema 对比的最佳实践:在迁移前对比"源 schema 与目标 schema"确认变更范围;在迁移后对比"实际 schema 与预期 schema"确认落地正确;定期对比生产与测试/基线环境检测漂移。对比对象应覆盖表、列、索引、约束、分区、视图、函数、存储过程等。实践要点:用自动化 diff 工具(migra、mysqldiff、pt-diff)避免人工遗漏;把对比纳入 CI/CD 流程(迁移后自动校验);对比结果作为迁移验收依据;对在线 DDL 的变更,对比时关注算法代价(INSTANT/INPLACE vs COPY)与是否影响业务。Schema 对比是迁移"可验证、可审计"的保障。

Schema 对比的实践核心是"迁移前后都对比 + 定期巡检 + 自动化"。在线 DDL 范畴下,对比还用于确认变更以最小代价落地。对比让迁移结果可验证、漂移可发现。

#
★★

41. 影子流量的实现(在迁移与在线 DDL 范畴内)?

在迁移与在线 DDL 范畴内,影子流量如何实现?

  • 影子流量迁移验证
  • CDC/复制实现
  • 与在线 DDL 结合

在迁移与在线 DDL 范畴内,影子流量用于验证新版本库/新结构在真实负载下的表现。实现:通过 CDC(解析 binlog/WAL)把生产增量同步到影子库,验证新库的数据处理与查询;或通过应用层双写把写操作同时写入新旧库,对比结果。影子流量让新库在"真实流量"下运行,观察其性能、锁、复制表现,发现潜在问题(如 SQL 变慢、锁竞争)。在线 DDL 场景,影子流量可验证新表结构(如新索引、新分区)在真实查询下的收益。影子流量的关键是"隔离":影子环境的流量不产生生产副作用。

影子流量在迁移中承担"验证师"角色,把真实流量引入新环境验证而不影响生产。CDC/双写是两种实现,隔离性是其核心价值。

#
★★

42. 在线改表完成后如何校验数据一致性(pt-table-checksum)?cut-over 之后发现问题如何回滚?

在线改表完成后如何校验数据一致性?cut-over 之后发现问题如何回滚?

  • pt-table-checksum 校验
  • 数据一致性校验方法
  • cut-over 后的回滚

在线改表完成后需校验数据一致性:用 pt-table-checksum 对主从或新旧表做逐行 checksum 对比,找出差异行;或对表做行数、校验和、抽样比对。pt-table-checksum 通过在主库上按 chunk 计算每行 checksum,同步到从库对比,能发现复制/改表造成的数据不一致。cut-over 之后发现问题,回滚策略:若 gh-ost/pt-osc 切换后仍保留旧表(或辅助表),可把旧表 RENAME 回原表名回滚;若旧表已删除,则需从备份恢复或重新执行迁移。因此在线改表前保留旧表(gh-ost 的 --serve-socket / 保留原表)作为回滚基线,是关键的回滚手段。回滚后还要校验数据一致性。

一致性校验(checksum 对账)是"改表成功"的验证,回滚依赖"切换前保留旧表"。两者结合保证在线改表"可验证、可回滚"。pt-table-checksum 是标准校验工具。

#
★★

43. Flyway 在数据库版本管理中的应用,V/R/U 脚本命名与校验和机制、baseline 与 repair 的适用场景,如何与 Expand-Contract 发布策略配合?

Flyway 在数据库版本管理中的应用是什么?V/R/U 脚本命名、校验和机制、baseline 与 repair 如何应用?如何与 Expand-Contract 配合?

  • V/R/U 脚本命名规范
  • 校验和(checksum)机制
  • baseline 与 repair

Flyway 用版本化 SQL 脚本管理 Schema。脚本命名:V 前缀(版本化迁移,如 V1__init.sql)、R 前缀(可重复迁移,如 R__views.sql,每次变更后重跑)、U 前缀(Undo 回滚,如 U1__xxx.sql,需手动配置)。Flyway 记录脚本的校验和(checksum),迁移后若脚本内容被修改,Flyway 会报校验和不匹配,防止已执行的迁移被篡改。baseline 用于"已存在的库"接入 Flyway(标记某个版本为基线,跳过之前脚本);repair 用于修复 schema_version 表与脚本状态不一致(如去掉已删除脚本的记录、清除校验和错误)。与 Expand-Contract 配合:Expand 阶段用 V 脚本加列(可回滚用 U 或再加新列),Contract 阶段用 V 脚本删旧列,利用 Flyway 的版本顺序保证所有环境按相同顺序执行,配合 Expand-Contract 的"先加后删"实现零停机发布。

Flyway 的价值是"脚本版本化 + 校验和防篡改 + 状态修复"。V/R/U 命名区分迁移类型,baseline 接入存量库,repair 修复状态。与 Expand-Contract 配合实现可控、可回滚的 Schema 演进。

#
★★

44. Liquibase 在企业数据库变更管理中的应用,changelog 的幂等执行与 checksum 校验,与 Flyway 在多人协作与复杂回滚上的差异如何选择?

Liquibase 在企业数据库变更管理中的应用是什么?changelog 的幂等执行与 checksum 校验如何工作?与 Flyway 在多人协作与复杂回滚上如何选择?

  • changelog 幂等执行
  • checksum 校验
  • 与 Flyway 的差异与选型

Liquibase 用 changelog(XML/YAML/SQL 等)描述数据库变更,支持跨数据库、幂等执行(每个 changeset 带 author+id 唯一标识,执行后记录到 DATABASECHANGELOG 表,重复执行会跳过已执行的 changeset),并用 checksum 校验 changeset 是否被修改过。Liquibase 的 changeset 支持 preConditions、context、rollback 语句,能定义复杂回滚。相比 Flyway:Liquibase 的 changelog 抽象更复杂、支持回滚与条件执行更完善,适合多人协作、复杂变更、跨数据库的大型企业环境;Flyway 简单直接、SQL 优先,适合普通项目。选型:追求简单用 Flyway,需要复杂回滚、多数据库抽象、企业级管控用 Liquibase。

Liquibase 的核心是"changeset 幂等 + checksum + 回滚能力"。它比 Flyway 更重视"可回滚、可条件化、跨库抽象",代价是更复杂。选型取决于团队对变更复杂度的需求。

#
★★

45. sqitch 的部署模型与 Flyway/Liquibase 有何不同,基于变更依赖图与回滚编排的设计思路,适合哪些多环境协作场景?

sqitch 的部署模型与 Flyway/Liquibase 有何不同?基于变更依赖图与回滚编排的设计思路是什么?适合哪些场景?

  • sqitch 的依赖图模型
  • 回滚编排
  • 与 Flyway/Liquibase 的差异与适用场景

sqitch 采用独特的"变更依赖图"模型:每个变更声明依赖(requires)与冲突(conflicts)的变更,形成有向图,sqitch 按依赖顺序部署(deploy),并支持按依赖逆序回滚(revert)。与 Flyway/Liquibase 的"顺序执行"不同,sqitch 不依赖版本号排序,而是依赖真实依赖关系,适合复杂依赖、需要精细回滚编排的场景。sqitch 的变更用三支脚本:deploy(部署)、revert(回滚)、verify(验证),每个变更可独立回滚与验证。适用场景:多环境协作、复杂 Schema 依赖、需要精确回滚顺序、不允许"版本号排序"的项目。代价是学习成本高、需手动维护依赖。

sqitch 的差异在于"依赖图替代版本号排序",回滚按依赖逆序编排,更符合复杂依赖的真实逻辑。它适合对回滚精确性要求高的多环境项目,代价是复杂度高。

#
★★

46. 跨库数据迁移后的校验与对账(抽样比对、计数比对、增量追平)

跨库数据迁移后如何校验与对账?抽样比对、计数比对、增量追平如何应用?

  • 计数比对
  • 抽样比对
  • 增量追平

跨库数据迁移后需校验对账,方法分层次:计数比对(先对比两库各表行数,快速发现数量级差异);抽样比对(对关键表抽样对比字段值,如 checksum、主键样本,发现值差异);增量追平(对迁移期间产生的增量数据,通过 CDC/双写/日志补采,把最后一段增量追平到新库,保证最终一致)。对账流程:先计数,再抽样,最后追平增量并二次对账。对账工具可用 pt-table-checksum、自定义 SQL、抽样比对脚本。对账通过后才可切换流量。对账解决"迁移是否完整一致"的验证问题。

对账是迁移"可验证"的保障。计数发现"量"的差异,抽样发现"值"的差异,增量追平解决"迁移期间的新数据",三层结合保证最终一致。

-- 计数比对
SELECT COUNT(*) FROM t_new;
SELECT COUNT(*) FROM t_old;
-- 抽样比对(按主键抽样对比字段)
SELECT id, col FROM t_new WHERE id % 100 = 0;
SELECT id, col FROM t_old WHERE id % 100 = 0;
#

47. 迁移窗口与发布窗口的编排(维护窗口、回滚条件与通知)

迁移窗口与发布窗口如何编排?维护窗口、回滚条件与通知如何设计?

  • 迁移窗口与发布窗口编排
  • 维护窗口的设定
  • 回滚条件与通知

迁移窗口与发布窗口的编排是把数据库迁移与应用发布安排在可控的时间窗口内,降低风险。维护窗口:选择业务低峰期(如凌晨),设定开始/结束时间与超时上限,窗口内执行迁移+发布。编排原则:库迁移(Expand 阶段)先于应用发布,应用发布(Contract 阶段)在库迁移稳定后进行;把"库变更"与"应用变更"解耦,避免互相阻塞。回滚条件:预先定义回滚触发条件(如迁移失败、数据不一致、应用错误率超阈值),并准备回滚预案(切回旧表、回滚迁移脚本)。通知:迁移前通知相关方,执行中通知进度,完成/失败/回滚及时通知,保留审计记录。整体编排要"可预测、可回滚、可通知"。

迁移与发布编排的核心是"时机 + 顺序 + 回滚 + 通知"。低峰窗口降低风险,Expand 先于 Contract 保证兼容,回滚条件让失败可恢复,通知保证协同。这些让变更可管控。