tenant_id字段必须全程参与所有ddl和dml操作,否则升级中易致跨租户数据污染或丢失;因大表alter table会锁表且租户业务节奏不一,需分批导出物理分片、单独执行变更并配合双写校验、pt-osc须严格限定tenant_id范围及索引,升级后须从结构、数据、行为三维度验证一致性,并维护tenant_schema_version元数据表。

tenant_id 字段必须全程参与所有 DDL 和 DML 操作,否则升级过程中极易出现跨租户数据污染或丢失。这是分批次平滑升级最根本的前提。
为什么不能直接跑全量 ALTER TABLE?
在共享库共享表(即所有租户共用同一张表、靠 tenant_id 隔离)架构下,对大表执行 ALTER TABLE 会锁表或阻塞写入,而租户间业务节奏不一致:有的租户正在做月结,有的刚上线新功能,有的处于低峰期。一刀切式 DDL 会导致部分租户服务不可用,且无法按租户灰度验证 Schema 变更效果。
如何按租户分批执行 ALTER TABLE?
MySQL 原生不支持“按条件分批改表”,但可通过以下组合策略实现逻辑分批:
- 先将目标表按
tenant_id分组导出为临时物理分片(如table_t1,table_t2),使用mysqldump --where="tenant_id IN (1,2,3)"或SELECT ... INTO OUTFILE+LOAD DATA - 对每个分片表单独执行
ALTER TABLE(加字段、改类型、建索引等),期间原表仍可读写(需应用层路由拦截该租户写入) - 用双写+校验机制同步变更:升级期间新写入走新表结构,旧写入走老表结构,通过定时任务比对
tenant_id+ 主键范围内的数据一致性 - 切换前确保该租户无长事务、无未提交的 DML,并在应用层配置中将该租户路由指向新分片表
pt-online-schema-change 能否直接用于多租户场景?
能,但必须配合租户维度控制,否则会误伤其他租户:
-
pt-online-schema-change默认作用于整张表,它创建的触发器会捕获所有INSERT/UPDATE/DELETE,包括其他租户的数据——这在租户隔离要求高的场景下是不可接受的 - 若坚持使用,必须在
--where参数中显式指定tenant_id = X,并确保该租户所有写请求已路由到专用连接池(避免混流) - 注意
pt-osc的--chunk-index必须包含tenant_id,否则分块扫描可能跨租户,导致锁范围失控 - 执行日志中要重点检查
Copying rows阶段是否只命中目标租户数据,可通过SHOW PROCESSLIST观察实际扫描的 WHERE 条件
升级后如何验证租户数据一致性?
不能只查单条记录或 COUNT(*),必须覆盖三个维度:
- 结构一致性:对比新旧表的
DESCRIBE table_name输出,确认字段类型、NULL 属性、默认值、索引是否全部生效 - 数据一致性:对每个已升级租户,抽样校验主键区间(如
id BETWEEN 1000 AND 2000)内tenant_id相同的记录,字段值、更新时间、逻辑删除标记是否完全一致 - 行为一致性:回放该租户最近 1 小时的 binlog(用
mysqlbinlog --base64-output=DECODE-ROWS -v),重放至测试库,确认所有 DML 在新 Schema 下能正确执行且无截断/隐式转换
tenant_schema_version 表,记录每个 tenant_id 当前对应的数据库版本号,否则下次升级时无法判断“这个租户到底升没升”,也难以支撑后续的反向兼容逻辑(比如老客户端连新表结构)。











