在线升级里的数据库脚本为什么要按版本顺序排,跑到一半失败该怎么办
在线升级里的数据库脚本为什么要按版本顺序排,是因为每条脚本都默认前一条已经执行完:某条脚本给表加新字段,下一条就往这个字段里写值,顺序一颠倒,后一条找不到字段直接报错;再往后跑还会把已经改过的结构继续按旧假设改写。至于跑到一半失败该怎么办,正确处置是立即中止并把失败位置记进日志,而不是跳过错误继续执行剩余脚本——剩下的脚本执行得越多,库结构和程序代码之间的距离就越远,排查成本也越高。
为什么顺序不能靠文件名以外的东西决定
升级脚本本质是一段按时间累积的依赖链。第 A 版把字段改名,第 B 版基于新名字建索引,第 C 版按新索引回填数据。程序代码读的是最末一版的结构,脚本却按各自的产出时间排列,两者能对齐全靠执行次序。所以同一批脚本放进同一个目录、按版本号从小到大一次跑完,是这类依赖链成立的前提。PbootCMS 在 V3.2.26 的更新记录里就明确写了这条约定:升级 SQL 统一放脚本目录并按版本序执行,原先散落在新装备份目录里的脚本只保留给全新安装使用。把「新装」和「升级」两套脚本分开,正是为了避免安装用的建表语句混进升级序列里重复执行。
跳过中间版本会留下什么
跨版本升级时最容易出问题的不是报错,而是看起来成功。常见表现有三类:字段缺失但程序读的是默认值,页面照常显示,统计数据却持续偏差;索引没建上,功能可用但查询越来越慢;老数据没按新规则回填,新旧内容在同一处显示时口径不一致。这类状态很难靠现象反推原因,因为它没有报错。可行的做法是让升级程序自己拒绝跳序执行:每次只允许从当前记录的版本号往后连续跑,版本号缺失就停下来问,而不是猜测该从哪一条开始。
失败时中止为什么比继续跑更安全
同行程序 V3.2.25 的修复项可以说明这个差别。此前的问题是迁移失败时既不记日志,报错也不指向具体脚本,升级仍然继续走完,管理员看到的提示是成功;修复后的行为是失败即记录日志并中止升级,同时跳过纯注释片段。两类做法的差别在于错误留在哪里:中止把错误停在第一个失败点,库结构只前进到那一步,回退时只需退到失败前的备份;继续跑则让错误散落到后续每一条脚本里,等发现异常时已经分不清是哪一步开始偏的。纯注释片段不该算失败,因为注释本身不改变结构,把它当成错误会中止掉本可以正常的升级,这也是这条修复记录里单独说明的原因。
失败之后按什么顺序处理
| 阶段 | 先做什么 | 判断依据 | 之后的动作 |
|---|---|---|---|
| 升级前 | 做完整备份 | 数据与静态文件都要覆盖,缺一项还原后站点不完整 | 记下当前版本号与脚本清单 |
| 失败当下 | 读日志定位脚本 | 日志应指明失败发生在哪一版哪一条 | 不重复执行整批脚本 |
| 修复期 | 单独处理失败脚本 | 确认是结构冲突、字段已存在还是权限问题 | 手工补齐该版变更,再继续后续版本 |
| 还原 | 回到备份点 | 结构无法对齐且短期内修不好 | 还原数据与静态文件,再排下次升级 |
| 核对 | 逐段验证 | 新字段、索引、历史数据回填三项各自检查 | 记录结果,保留日志 |
升级前后各核对什么
升级前的备份要同时覆盖数据和静态文件,只备数据库的话,上传目录与模板改动无法一并还原。AnQiCMS(安企CMS)把这一点做成后台能力:备份与恢复支持数据以及静态文件,升级前先在后台生成一份,再开始执行脚本。部署形态也影响操作方式,用宝塔面板或 Docker 镜像部署的站点可以在升级前多留一层快照,容器方式还便于整体回退到上一个镜像版本。
升级后的核对按三项走:结构项看新增字段与索引是否到位;数据项看历史内容是否按新规则回填;行为项看列表、详情、表单提交三条主路径是否正常。失败提示要有去处,站内支持邮件提醒,把升级相关的通知目标提前配好,比事后翻日志更早发现问题。核对完再清理临时脚本文件,避免下次升级时被误当成待执行项。
常见问题
问:升级脚本能不能自己改名来控制执行顺序? 答:改名只影响文件排序,不影响脚本内容里的版本判断。顺序应交给升级程序按记录的版本号决定,人工改名容易让记录与实际执行的文件对不上。
问:报错说字段已存在,可以忽略吗? 答:先确认这条脚本是否已经执行过。若确实执行过,说明版本号记录与实际结构不一致,应把记录修正到正确版本再继续;直接忽略则可能跳过真正没执行的那一条。
问:中断之后能重跑整批脚本吗? 答:取决于脚本是否可重复执行。带「不存在才创建」判断的可以,直接建表或改名的一重试就报错。重跑前建议先备份,再按日志从失败位置逐条判断。