迁移在开发库上一秒完成,到了生产大表上会锁多久?这篇造一张 200 万行的表,让一个线程持续写入,再对它执行 7 种常见迁移操作,把「写入被阻塞了多久」量出来。
操作 结果 AddField(int, default=0, NOT NULL) 迁移总用时 0.28 s 期间写入最长一次 1 ms(平时中位 0.2 ms) AddField(int, null=True) 迁移总用时 0.27 s 期间写入最长一次 4 ms(平时中位 0.3 ms) AlterField varchar(20) → varchar(60) 迁移总用时 0.29 s 期间写入最长一次 2 ms(平时中位 0.3 ms) AlterField integer → bigint(重写整张表) 迁移总用时 1.51 s 期间写入最长一次 1220 ms(平时中位 0.2 ms) AlterField null → NOT NULL(扫全表) 迁移总用时 0.56 s 期间写入最长一次 286 ms(平时中位 0.2 ms) AddIndex(普通 CREATE INDEX) 迁移总用时 0.87 s 期间写入最长一次 587 ms(平时中位 0.4 ms) AddIndexConcurrently(CREATE INDEX CONCURRENTLY) 迁移总用时 0.70 s 期间写入最长一次 1 ms(平时中位 0.3 ms)
「迁移总用时」包含 Django 命令自己的启动(约 0.25 s)。「期间写入最长一次」是后台线程在迁移进行期间(前后各留 0.3 s 余量)观察到的单次 UPDATE 最长耗时,平时中位数约 0.2–0.4 ms。
AddIndexConcurrently(迁移要 atomic = False,失败后可能留下无效索引,要手工清理——这一点本文没有验证)。