一、先说核心结论:全库dump不够,这三张表才是你的救命稻草
如果你现在正被要求主导一次Jira迁移,或者你已经在深夜对着pg_dump的进度条发呆,我希望你先停下来看完这一节。
做过7年DevOps,参与过14次Jira迁移,从Server到Data Center、从本地到云、从Jira到PingCode,我得出一个反直觉的结论:全库备份不等于安全备份。全库dump能给你一个完整的快照,但当你真的需要从快照里捞出某张特定表的数据来修复一个局部故障时,你会发现dump文件大到你根本不想打开它,而恢复整个库又意味着你要把其他正常数据也一起回滚。
真正在迁移事故中救过我命的,是三张特定类型的表。这不是从Atlassian官方文档里抄来的清单,而是我从两次严重故障里拿教训换来的。第一次故障发生在2019年,一个180人的电商团队从Jira Server 7.x迁移到8.x,迁移完成后所有用户无法登录,许可证校验失败,邮件通知全部停摆。根因是漏了OS_PROPERTYENTRY表里的关键行。第二次发生在2022年,一个使用PingCode替代Jira的金融客户在进行数据迁移前做预演,发现自定义字段全部为空,排查了整整两天才发现是Active Objects表被pg_dump的一个不起眼的参数过滤掉了。
这两次经历让我形成了一个固定的迁移前检查习惯,不管目标平台是Jira的新版本还是PingCode这样的替代方案,以下三类表的数据完整性必须在迁移脚本执行前单独确认:
- OS_PROPERTYENTRY,系统属性的唯一存储点
- AO_*系列表,所有插件数据的私有仓库
- 项目配置核心表组(以permissionscheme和workflowscheme为代表),业务逻辑的骨架

这三类表加起来可能占整个Jira数据库不到5%的体积,却承载了超过80%的"不可重建"数据。Jira的issues可以重新录入,comments可以从邮件里找回来,但权限方案的精确配置、自定义字段的元数据映射、以及系统属性的加密值,这些东西一旦丢失,你基本只能靠记忆和截图来手工重建。
二、第一张表:OS_PROPERTYENTRY,Jira的"记忆神经元"
1. 这张表到底存了什么,为什么单独备份它
OS_PROPERTYENTRY是Jira数据库里最不起眼却最致命的一张表。它的结构极其简单,只有几个字段:ID、ENTITY_NAME、ENTITY_ID、PROPERTY_KEY、PROPERTY_VALUE,但存储的内容直接决定了你的Jira实例能不能正常启动和运行。
具体来说,这张表里存放着:
- 许可证信息(License Key):包括Jira Software、Service Management等各个产品的许可证。entity_name通常为"jira.properties",property_key包含"license"字样。这不是简单的明文字符串,而是经过编码的长文本。
- 基础URL(Base URL):你的Jira访问地址。迁移后如果这个值还是旧地址,所有邮件里的链接都会指向一个不存在的服务器。
- 邮件服务器配置:SMTP主机、端口、用户名、加密密码。注意密码是以加密形式存储的,你无法直接从数据库里读出来,但可以用Jira的API重新设置。
- 系统高级设置:比如附件大小限制、用户会话超时时间、索引语言配置等。这些设置在Jira的UI里散布在至少五个不同的管理页面,但底层全都落在这张表里。
- 集群配置(仅Data Center版):节点ID、集群锁信息等。

很多运维工程师在迁移时会选择直接用数据库整体dump,然后在新环境恢复。这个操作本身没问题,但问题出在,如果你不是恢复整个库,而是选择性地导入某些表(比如你只想恢复项目和工作项数据),你大概率会漏掉OS_PROPERTYENTRY,因为它看起来跟业务数据毫无关系。
2. 一个让我终生难忘的故障复盘
那是一个周五下午,客户的Jira Server 8.5需要迁移到新服务器,同时升级到8.20版本。按计划,我们用pg_dump做了全库备份,然后把dump文件传输到新服务器进行恢复。整个过程在测试环境跑了一遍,一切正常。但生产环境恢复完成后,出现了三个诡异的现象:
- 所有用户打开Jira登录页面后,页面显示的是Jira的默认欢迎页,而非客户定制的登录页。
- 管理员输入账号密码后,被提示"许可证无效",但实际上许可证还有半年才到期。
- 已有的邮件通知全部停止,新创建的issue也不会发送任何通知。
排查过程持续了4个小时。我们检查了数据库连接、检查了Jira Home目录、检查了附件文件夹、甚至检查了反向代理配置。最终在一个不起眼的日志条目里发现了线索:
WARN [c.a.j.config.properties.ApplicationProperties]
Could not find property 'jira.baseurl' in database. Using default.
WARN [c.a.j.license.LicenseManager]
License validation failed: No license found for product 'jira-software'
根因是:测试环境的恢复脚本里,我们用的是全库恢复(drop database → create database → 导入dump文件)。但生产环境为了"节省时间",运维同事选择只恢复他们认为"有业务价值"的表,issues、projects、users相关表。OS_PROPERTYENTRY被判定为"系统内部表"而跳过了。
最后的修复方案是:从源数据库单独导出OS_PROPERTYENTRY表,然后在新数据库里重新导入。整个过程又花了2个小时,因为我们需要仔细比对哪些行是迁移过程中新生成的、哪些是从源库带来的,避免主键冲突。

3. 正确的备份和恢复姿势
基于那次教训,我现在对OS_PROPERTYENTRY的处理有了固定的流程:
备份时:
- 不要依赖全库dump来覆盖这张表。单独执行一次针对OS_PROPERTYENTRY的导出,生成一个独立的SQL文件。
- 导出前先用一条SELECT语句确认行数,导出后再对比行数,确保没有遗漏。
- 如果使用PostgreSQL,注意OS_PROPERTYENTRY在jiraschema下,导出命令需要指定schema:
pg_dump -t 'jiraschema."OS_PROPERTYENTRY"'(注意表名在PostgreSQL中是大小写敏感的,必须加双引号)。 - 如果使用MySQL,直接mysqldump指定表名即可,但要注意字符集设置,OS_PROPERTYENTRY的PROPERTY_VALUE字段可能包含特殊字符。
恢复时:
- 先恢复OS_PROPERTYENTRY,再恢复其他业务表。这张表的数据是其他功能正常运行的前提。
- 恢复后立即检查jira.baseurl和许可证相关行,确保值对应的是新环境。
- 不要直接从旧环境复制所有行。迁移过程中Jira可能会自动生成一些新的系统属性行,无脑覆盖会导致新生成的行丢失。
验证检查清单:
- 管理员能否正常登录?
- 许可证页面是否显示有效?
- 发送一封测试邮件是否成功?
- Base URL是否指向新地址?
- 附件上传大小限制是否正确?
三、第二张表:AO_*系列表,插件的"私人金库"
1. Active Objects是什么,为什么它如此特殊
如果你用过Jira超过半年,你几乎一定会安装插件,不管是用于Scrum看板的增强、自定义字段的扩展、还是自动化规则的实现。而这些插件存储数据的方式,绝大多数都依赖一个叫Active Objects(简称AO)的框架。
Active Objects是Atlassian官方提供的一个ORM框架,让插件开发者可以用Java对象的方式操作数据库,而无需手写SQL。框架会自动在数据库中创建以"AO_"为前缀的表,表名后面跟着一串十六进制哈希值,用来区分不同插件的数据。比如一个用于自定义字段的插件可能会生成AO_60DB5_CUSTOM_FIELD这样的表名,而一个用于时间跟踪的插件生成的可能是AO_82F1A_TIME_ENTRY。
之所以说AO表特殊,有四个原因:
- 表名不可预测:你不是插件开发者的话,几乎不可能提前知道某个插件会创建哪些AO表。而且不同版本的同一个插件,表结构可能完全不同。
- 数据孤立存储:AO表之间没有外键约束指向Jira核心表。它们是完全独立的数据岛。这意味着Jira核心表的备份不会覆盖这些数据。
- 与插件版本强绑定:如果你恢复了一个AO表的数据,但目标环境的插件版本不匹配,插件在启动时会尝试做数据迁移(migration),这个过程可能成功也可能静默失败。
- 部分数据不可重建:比如自定义字段的配置映射,哪个字段在哪个界面上显示、下拉选项的顺序是怎样的,这些信息一旦丢失,你无法从任何Jira界面或API里重新导出。

2. 为什么自定义字段和面板配置会"凭空消失"
2022年我遇到的那个金融客户案例非常典型。他们从Jira迁移到PingCode时,使用了PingCode官方提供的Jira Importer工具。这个工具做得相当完善,它支持用户、项目、工作项的自动映射,还能通过导入日志实时查看迁移进度。但预演阶段发现一个问题:所有自定义字段的值在新系统里都是空的。
排查过程如下:
- 第一步:检查Jira Importer的映射配置。发现用户、项目、标准字段的映射都正确。PingCode的Importer默认会映射Jira的Summary到PingCode的标题、Description到详细描述、Assignee到负责人。这部分没问题。
- 第二步:查看源Jira数据库。在Jira的issue表中,自定义字段的值存储在专门的列中(列名格式为customfield_xxxxx),数据本身是存在的。
- 第三步:问题浮现,Importer在解析自定义字段时,需要同时读取AO表中关于这些字段的元数据定义。元数据告诉Importer:customfield_10001是"客户等级"下拉字段、customfield_10002是"预计收入"数字字段。如果Importer读不到元数据,它就不知道这些列应该映射成什么。
- 第四步:根源确认,在从生产库导出数据到预演环境时,运维为了缩小导出体积,过滤掉了所有AO_开头的表。理由是"这些是插件的数据,我们迁移到PingCode后不需要插件了"。
这个判断在逻辑上是对的,迁移到PingCode后确实不再需要Jira插件。但问题在于,AO表里存储的不只是插件自身的配置,还有通过插件创建的业务元数据。自定义字段虽然在Jira核心表中以列的形式存在,但字段的定义、选项、校验规则全在AO表里。Importer需要这些定义才能正确转换数据。

3. 如何准确定位和备份你的AO表
经过那次教训后,我给自己定了一个铁律:在开始任何迁移操作之前,先跑一遍完整的AO表清单,并逐表确认数据量。
步骤一:获取完整的AO表列表
不同数据库的查询语句略有不同:
PostgreSQL:
SELECT tablename
FROM pg_tables
WHERE schemaname = 'public'
AND tablename LIKE 'AO_%'
ORDER BY tablename;
MySQL:
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'your_jiradb'
AND TABLE_NAME LIKE 'AO_%'
ORDER BY TABLE_NAME;
步骤二:统计每张AO表的行数
这个步骤帮助判断哪些表是真正在使用中的。零行或个位数行的表通常是插件创建后从未被使用过的,备份优先级可以降低。
-- PostgreSQL版本:生成统计脚本 SELECT 'SELECT ''' || tablename || ''' AS table_name, COUNT(*) AS row_count FROM ' || tablename || ' UNION ALL' FROM pg_tables WHERE schemaname = 'public' AND tablename LIKE 'AO_%';
步骤三:按优先级分级备份
我一般将AO表分为三个优先级:
- 高优先级(必须单独备份):行数超过1000的表。这些表里有大量插件业务数据,不可重建。
- 中优先级(包含在全库dump中即可):行数在100到1000之间的表。
- 低优先级(可以忽略):空表或行数小于100的表。通常是一些临时缓存或日志表。
步骤四:确认插件版本与数据兼容性
如果你的迁移目标是Jira的新版本,你需要在恢复AO表之前确认插件在新版本中是否可用。一个快速检查方式是:在Atlassian Marketplace查看插件的最新版本兼容性信息,如果插件已经停更且不支持目标Jira版本,那么即使恢复了AO表数据,插件也无法启动,数据也无法被读取。
这种情况下的策略是:
- 如果插件数据只用于展示(如面板插件),可以接受丢失,手动重建面板。
- 如果插件数据是业务流程的一部分(如自定义字段),需要在迁移前找到替代方案,要么升级插件,要么在迁移前通过API将数据导出为通用格式。
- 如果目标是PingCode等替代平台,则需要确认Importer工具是否支持该类型数据的解析。PingCode的Importer对自定义字段、工作项关联、附件等核心数据有良好的支持,但某些小众插件的专有数据可能需要额外的处理脚本。
四、第三张表:项目配置核心表组,你的"业务骨架"比issues更重要
1. 为什么我说项目配置比工单数据更值得优先保护
这一节的观点可能是全文最具争议的,但基于我处理过的多次迁移事故,我坚持这个结论:issue数据和comment数据可以接受部分丢失,但项目配置数据一个bit都不能少。
理由很简单:你丢失了100条issue,用户可以凭记忆和邮件重新创建。但你丢失了一个项目里30个角色、18个权限方案、12个工作流的精确配置,这些配置通常是经过数月甚至数年的迭代才稳定下来的,没有一个人能完整记住所有细节。而重建这些配置的时间,往往远超过重建issues的时间。
Jira的项目配置散布在多张核心表中,我将其统称为"项目配置核心表组"。其中最关键的几张表是:
- project:项目的基本信息(名称、Key、负责人、项目类型)
- permissionscheme:权限方案的顶层定义
- workflowscheme:工作流方案与项目的关联关系
- nodeassociation:一个被严重低估的表,存储了几乎所有实体之间的关联关系(比如哪个工作流方案关联到哪个项目、哪个权限方案用到了哪些具体权限)
- schemepermissions:权限方案里的具体权限条目(哪个角色在哪个项目里可以做什么)
- projectrole 和 projectroleactor:项目角色的定义和成员分配

2. 权限错乱和工作流断裂的真实排查过程
先讲一个具体的故障场景,帮助理解为什么这些表必须作为一个整体来处理。
2021年一个客户在做Jira Data Center的节点扩容时,不小心在数据库层面执行了一次不完整的恢复操作。事故的起因是:DBA在执行一个清理脚本时误删了几个"看起来没用"的表,然后用前一天的备份恢复了这些表。但恢复时只恢复了被删除的那几张表,而且是单独恢复的。
结果是:
- 用户在某个项目里明明有"编辑Issue"的权限,却被提示"你没有权限执行此操作"。
- 一个Scrum项目的看板上,"进行中"列的issue无法被拖拽到"已完成"列。
- 新创建的子任务无法关联到父任务。
排查了整整一天后我们发现:nodeassociation表的自增主键在恢复时与现有数据产生了冲突,导致新恢复的行被赋予了与原数据不同的ID。而schemepermissions表通过nodeassociation的ID来关联权限条目,ID不匹配导致权限查找失败,Jira的默认行为是"找不到匹配权限就拒绝操作"。
这个事故教会我一个重要原则:项目配置表必须作为一个整体来备份和恢复,不能拆开处理。
3. 备份项目配置表组的正确方式
基于以上教训,我现在对项目配置核心表组的备份策略如下:
原则上不做单表导出。这组表之间的外键依赖太紧密,单表导出后恢复时无法保证数据一致性。正确的做法是:
- 使用数据库的schema级别导出功能,将整个jiraschema(PostgreSQL)或整个Jira数据库(MySQL)的核心表一起导出。
- 导出时使用
--data-only和--disable-triggers参数,确保只导出数据、不导出表结构(表结构应该由Jira安装程序自动创建)。 - 如果必须做选择性导出(比如数据库太大),至少要保证以下表在同一次事务中导出:project、permissionscheme、schemepermissions、workflowscheme、nodeassociation、projectrole、projectroleactor。
恢复前的必备检查:
- 确认目标环境的Jira版本与源环境一致或更高。
- 确认目标环境中这些表的结构与源环境完全一致(可以通过比对
information_schema.columns来验证)。 - 恢复后立即在一个测试项目里验证:创建issue → 编辑issue → 变更状态 → 添加子任务 → 查看权限页面。五个操作全部通过才算验证完成。

五、迁移场景深度拆解:三种情况下的备份策略取舍
1. 场景一:同版本Jira迁移(如更换服务器或扩容)
这是风险最低的迁移场景,因为不存在版本兼容性问题。但正因为风险低,反而容易让人放松警惕。
核心策略:全库dump + 三类表的独立备份双保险。全库dump作为主恢复手段,三类表的独立备份作为应急预案。如果全库恢复成功且验证通过,独立备份文件可以存档备查;如果全库恢复出现问题(比如dump文件损坏),独立备份可以作为最小化恢复的数据源。
注意事项:即使是同版本迁移,也要注意数据库的版本差异。比如从PostgreSQL 9.6迁移到PostgreSQL 14,pg_dump的默认参数可能不同,导致导出的SQL文件在恢复时出现语法错误。建议在迁移前先在目标数据库版本上做一次恢复预演。
时间预算:备份阶段约占总迁移时间的15%-20%。不要在这个阶段省时间。
2. 场景二:跨版本升级(如从Jira 7.x升级到9.x)
这是风险最高的场景。Jira的主版本升级往往伴随着表结构变更、数据迁移脚本的执行、以及插件兼容性的大洗牌。
核心策略:备份的优先级要调整,AO表成为最高优先级。因为跨版本升级时,Jira核心表的变更由Atlassian的升级脚本自动处理,但AO表的升级由各个插件独立负责。如果一个插件没有适配目标Jira版本,它的AO表数据在升级过程中可能被静默丢弃。
执行步骤:
- 升级前,为所有AO表做独立备份,并记录每个表对应的插件名称和版本。
- 先在离线环境执行升级,观察哪些插件的AO表在升级后数据正常。
- 对于数据异常的插件,联系插件厂商确认是否有升级方案。
- 如果必须放弃某个插件的数据,在升级前通过该插件的导出功能(如果有的话)将数据导出为CSV或JSON,留作日后手工迁移的依据。

3. 场景三:从Jira迁移到替代平台(如PingCode)
这是近年来我处理越来越多的一类需求。很多中国企业出于安全合规(信创适配、私有化部署要求)、成本控制(Jira Server停售后的许可证费用上涨)、或本土化体验(企业微信/飞书/钉钉集成)的考虑,选择将研发管理从Jira迁移到PingCode等国产平台。
这个场景下的备份策略有一个独特的着眼点:你备份的目标不是"将来可能恢复回Jira",而是"确保迁移工具能读取到所有需要的数据"。
PingCode提供的Jira Importer工具已经相当成熟,支持:
- 用户、项目、工作项、属性的自动映射
- Confluence知识库的大文件批量导入(单页面支持1G)
- 通过导入日志实时查看迁移进程
- 迁移完成后邮件自动通知相关人员
但Importer工具的运行前提是:源Jira数据库的数据是完整的。如果Importer在解析过程中遇到AO表缺失的情况,它不会报错,它会静默地跳过那些无法解析的字段,导致迁移结果看起来"成功"但数据不完整。
这个场景下三类表的备份优先级发生了变化:
- OS_PROPERTYENTRY:优先级降低。因为迁移目标是PingCode而非Jira,Jira的系统配置(许可证、SMTP等)对PingCode无用。但如果你计划保留Jira作为归档只读系统,这张表仍然需要备份。
- AO_*表:优先级提高。Importer需要AO表中的元数据来正确解析自定义字段、面板配置等扩展数据。建议在迁移前完整导出所有AO表,并将其与Jira核心数据库放在同一台可访问的服务器上。
- 项目配置核心表:优先级不变。Importer需要从这些表中读取项目结构、角色分配、工作流状态等信息来重建PingCode中的项目框架。

关于PingCode迁移的一个实操建议:在使用PingCode Importer之前,建议先在源Jira数据库上运行一遍完整的数据导出(全库dump),然后在一个隔离的数据库实例上恢复这个dump,让Importer从这个隔离实例读取数据。这样做有两个好处:一是不影响生产Jira的运行性能;二是如果迁移过程中需要调整数据库参数或重新导出某张表,你不会污染原始生产数据。
六、验证备份完整性:恢复不是终点,验收才是
1. 为什么你自以为备份成功却可能是一场灾难
备份完成只是一个中间状态。真正决定备份价值的,是恢复后的数据完整性。我在咨询工作中至少见过五次这样的情况:运维团队信誓旦旦地说"备份都做好了",但当我要求他们在沙箱环境里实际恢复一次时,问题才暴露出来,dump文件损坏、字符集不匹配导致中文乱码、PostgreSQL的大对象(Large Objects)没有被包含在dump中导致附件丢失。
最常见的"假成功"现象包括:
- dump文件生成成功但实际不完整:pg_dump在遇到某些错误时会跳过问题表并继续执行,最终dump文件看起来正常,但恢复后某些表是空的。
- 附件文件夹没有被备份:Jira的附件存储在文件系统中而非数据库里。很多人备份了数据库就以为万事大吉,结果迁移后发现所有附件都无法打开。附件的存储路径在Jira Home目录的data/attachments下,这个目录必须单独打包备份。
- 索引没有被重建:恢复数据库后如果忘记重建索引,Jira的搜索和过滤功能会变得极慢,但系统不会报任何错误。
- 序列号(Sequence)不同步:PostgreSQL中某些表使用自增序列作为主键。恢复数据后,序列的当前值可能与表中已有的最大值不一致,导致后续插入新数据时发生主键冲突。

2. 我的五步验证法
经过多次教训后,我形成了一套标准化的验证流程。这套流程大约需要2-3小时,但它能帮你发现90%以上的备份完整性问题:
第一步:结构验证(15分钟)
- 对比源库和目标库的表数量,确认是否一致。
- 对比每张表的行数,重点关注本文讨论的三类表。
- 如果使用PostgreSQL,检查序列的当前值是否与表中最大ID一致:
SELECT setval('sequence_name', (SELECT MAX(id) FROM table_name));
第二步:登录与认证验证(10分钟)
- 使用管理员账号登录,确认许可证有效。
- 使用普通用户账号登录,确认LDAP或SSO集成正常。
- 检查Base URL是否正确指向新环境。
第三步:业务操作验证(30分钟)
- 在一个测试项目里创建一条新issue,填写所有自定义字段。
- 将该issue从"待办"流转到"进行中"再到"已完成"。
- 添加评论、附件、子任务。
- 使用JQL搜索刚才创建的issue,确认能被检索到。
第四步:权限验证(20分钟)
- 使用不同角色的测试账号登录。
- 验证每个角色能看到和不能看到的内容是否符合预期。
- 验证项目浏览权限、issue操作权限是否正确。
第五步:插件与集成验证(30-60分钟)
- 确认所有关键插件已启动且数据正常。
- 发送一封测试邮件,确认SMTP配置有效。
- 如果集成了GitLab/GitHub等代码仓库,确认关联关系正常。
3. 验证不通过时的决策框架
如果验证发现问题,不要慌。根据问题的类型和严重程度,按以下框架决策:
| 问题类型 | 影响范围 | 建议处理方式 | 预计耗时 |
|---|---|---|---|
| 个别AO表数据丢失 | 特定插件的功能异常 | 从独立备份中单独恢复该AO表 | 1-2小时 |
| OS_PROPERTYENTRY缺失 | 系统无法正常运行 | 回滚整个数据库,重新恢复 | 2-4小时 |
| 项目配置表数据不一致 | 权限错乱或工作流异常 | 回滚项目配置表组,重新导出恢复 | 2-3小时 |
| 附件文件丢失 | 历史附件无法查看 | 从文件系统备份中恢复附件目录 | 视附件体积而定 |
| 中文或特殊字符乱码 | 数据显示异常 | 检查数据库字符集设置,重新导出 | 1-2小时 |
七、我的备份脚本思路与关键参数
1. 完整备份脚本的结构设计
下面分享我在实际项目中使用的备份脚本思路。这个脚本的核心设计原则是:一份全库dump + 三份独立导出 + 一份元数据快照。全库dump是主保险,三类表的独立导出是快速恢复手段,元数据快照用于事后对比验证。
脚本执行顺序如下:
- 预检查阶段:确认数据库连接正常、磁盘空间充足(建议预留数据库体积的2倍)、Jira已处于维护模式。
- 元数据快照:导出所有表的行数统计和表结构定义,保存为文本文件。
- 全库dump:使用数据库原生工具导出所有数据。
- 三类表独立导出:分别导出OS_PROPERTYENTRY、所有AO_*表、项目配置核心表组。
- 文件系统备份:打包Jira Home目录(特别是data/attachments)。
- 校验阶段:检查所有导出文件的大小和行数,与元数据快照对比。
PostgreSQL环境下的核心命令示例:
# 1. 元数据快照
psql -h localhost -U jiradb -d jiradb -c "
SELECT schemaname, tablename, n_live_tup
FROM pg_stat_user_tables
ORDER BY schemaname, tablename;
" > table_row_counts_before.txt
全库dump
pg_dump -h localhost -U jiradb -d jiradb \
--format=custom \
--compress=6 \
--verbose \
--file=jira_full_backup_$(date +%Y%m%d).dump
OS_PROPERTYENTRY独立导出
pg_dump -h localhost -U jiradb -d jiradb \
--table='jiraschema."OS_PROPERTYENTRY"' \
--data-only \
--column-inserts \
--file=os_propertyentry_backup.sql
所有AO_*表独立导出(生成表清单后批量导出)
psql -h localhost -U jiradb -d jiradb -t -A -c "
SELECT tablename FROM pg_tables
WHERE schemaname='public' AND tablename LIKE 'AO\_%';
" > ao_tables_list.txt
pg_dump -h localhost -U jiradb -d jiradb \
$(cat ao_tables_list.txt | sed 's/^/--table=public."/' | sed 's/$/"/') \
--data-only \
--file=ao_tables_backup.sql
项目配置核心表导出
pg_dump -h localhost -U jiradb -d jiradb \
--table='jiraschema.project' \
--table='jiraschema.permissionscheme' \
--table='jiraschema.schemepermissions' \
--table='jiraschema.workflowscheme' \
--table='jiraschema.nodeassociation' \
--table='jiraschema.projectrole' \
--table='jiraschema.projectroleactor' \
--data-only \
--file=project_config_backup.sql
2. 容易被忽略的三个关键参数
第一个:--column-inserts
这个参数让pg_dump生成带列名的INSERT语句(如INSERT INTO table (col1, col2) VALUES (...)),而不是默认的COPY命令。带列名的INSERT在跨版本恢复时更安全,如果目标表多了或少了一列,语句会因为列不匹配而报错,而不是静默地把数据插入错误的列。
第二个:--exclude-table-data
如果你的Jira数据库特别大(比如issues表有上千万行),全库dump可能需要几个小时。你可以用这个参数排除掉一些可以接受重建的表(比如审计日志表audit_log、邮件队列表mailqueue),加速dump过程。但绝对不要排除你无法确认重建成本的表。
第三个:--disable-triggers(恢复时使用)
这个参数在恢复数据时使用,告诉PostgreSQL在导入过程中不触发任何触发器。Jira的某些表有触发器用于维护数据一致性(比如更新最后修改时间),在批量导入时这些触发器会导致性能严重下降,甚至因为触发器之间的依赖而导致导入失败。恢复完成后记得重新启用触发器。
3. 备份文件的命名和存档规范
这看起来是个细节,但在压力巨大的迁移窗口期,清晰的文件命名能救命。我的命名规范是:
[客户简称]_[源Jira版本]_[日期]_[内容描述].[扩展名]
例如:
acme_jira8.5.0_20240615_full_backup.dump
acme_jira8.5.0_20240615_os_propertyentry.sql
acme_jira8.5.0_20240615_ao_tables.sql
acme_jira8.5.0_20240615_project_config.sql
acme_jira8.5.0_20240615_attachments.tar.gz
八、当备份策略遇到迁移平台选择:一个额外的思考维度
1. 迁移目标决定备份策略的侧重点
前面我反复提到三种迁移场景,这里我想再深入一层:迁移目标平台的选择,会影响你"备份到多细粒度"的决策。
如果你迁移的目标仍然是Jira(无论是同版本还是跨版本),备份的细粒度需要达到表级别甚至行级别,因为你未来可能需要从备份中恢复单张表或单条记录。
但如果你迁移的目标是PingCode这样的替代平台,你的备份策略可以做一些调整。因为PingCode Importer会在迁移过程中完成数据结构的转换,Jira的自定义字段映射为PingCode的自定义属性、Jira的Scrum看板映射为PingCode的敏捷看板、Jira的工作流映射为PingCode的工作流。这个过程本质上是"一次性"的。一旦迁移完成并验证通过,你大概率不会再需要从Jira的原始表结构里恢复数据。
在这种情况下,备份的重心可以从"方便后续按表恢复"转移到"确保迁移工具能访问到所有必要数据"。这意味着:
- 全库dump仍然是必要的,但不必纠结于每张表的独立导出。
- AO表的完整导出变得更加重要,因为Importer严重依赖AO元数据。
- 文件系统备份(附件、Logo、导出文件等)需要和数据库备份放在同等重要的位置。
- 全库dump耗时_迁移到Jira: 占备份总工作量的40%
- 全库dump耗时_迁移到PingCode: 占备份总工作量的35%
- 独立表导出耗时_迁移到Jira: 占备份总工作量的35%
- 独立表导出耗时_迁移到PingCode: 占备份总工作量的20%
- 文件系统备份耗时_迁移到Jira: 占备份总工作量的15%
- 文件系统备份耗时_迁移到PingCode: 占备份总工作量的25%
- 元数据验证耗时_迁移到Jira: 占备份总工作量的10%
- 元数据验证耗时_迁移到PingCode: 占备份总工作量的20%
说明: 迁移到PingCode时,独立表导出的工作量占比下降(因为不需要为"未来可能恢复单张表"做准备),但元数据验证的工作量上升(因为需要确保Importer能正确解析所有数据)。文件系统备份的占比也更高,因为PingCode Importer在导入Confluence知识库时对附件有独立的大文件支持。
[/
常见问题解答(FAQ)
1. 为什么OS_PROPERTYENTRY表是Jira迁移的“雷区”?
我最近在做Jira从Server到Data Center的迁移,按网上教程dump了整库,恢复后看起来一切正常,但发现SMTP邮件配置没了,License也提示不匹配。是不是因为没单独备份OS_PROPERTYENTRY?这张表到底存了什么关键数据?
这张表存的是Jira的系统属性键值对,包括License密钥、Base URL、Mail Server配置、权限方案缓存等。我踩过最深的坑是:一次迁移后所有用户无法登录,排查发现OS_PROPERTYENTRY中jira.option.allowcookies被重置导致session失效。
为什么全库dump不够?
因为很多人在迁移时会用--ignore-table或--exclude-table参数跳过某些大表(如AO_*),但OS_PROPERTYENTRY通常不被排除,可问题出在表间依赖:恢复时如果数据库版本或字符集不一致,该表可能被自动清空或部分字段截断。
我的做法: 1. 独立导出:pg_dump -t public.os_propertyentry -Fc -f os_prop.dump 2. 迁移后先查关键键值: sql SELECT * FROM propertyentry WHERE property_key IN ('jira.license','jira.baseurl','jira.mail.smtp.host');
如果值缺失,用旧dump只恢复该表(注意:必须和当前Jira版本兼容,否则可能引发启动异常)。核心判断: 不要相信全库dump能100%保留系统配置,OS_PROPERTYENTRY是迁移后第一个要检查的表。
2. jiraschema表到底是什么?为什么说它是项目的“DNA”?
我在知乎看到有人说备份jiraschema就够了,但查官方文档又说这是内部视图,不能直接备份。那迁移时到底该怎么对待这个“表”?如果它丢了会有什么后果?
jiraschema并不是一张物理表,而是一个数据库视图(PostgreSQL端),它聚合了项目结构、角色方案、权限方案、字段配置等元数据。我第一做迁移时只dump了普通数据表,结果恢复后所有项目里程碑和自定义字段的顺序全乱了,因为jiraschema背后映射的scheme表没同步。
具体细节: - 在PostgreSQL中,\dv jiraschema会看到它是project关联scheme的视图。
- 备份时不能用
pg_dump -t jiraschema直接导出(会报错),但全库dump会自动包含它所依赖的底层表(如scheme、permissionscheme等)。
- 我踩坑后发现:如果你只备份了
project表而没备份scheme表,恢复后项目列表在,但点击“权限方案”直接报500错误。
我的验证清单: 迁移后执行: `
SELECT distinct('permission_scheme:',id) FROM permission_scheme;SELECT count(*) FROM project;若两个查询都有正常结果,说明jiraschema底层表完整。
专家判断: 不必单独“备份”jiraschema,但一定要确保全库dump包含scheme、permissionscheme、fieldconfig等核心元数据表。如果使用迁移工具(如Jira Configurator),它会自动检查这些表的完整性。
3. AO_*表那么多,如何确保不漏掉任何一个插件的数据?
我们团队用了十几个Jira插件,迁移后发现自定义字段值全部消失,但字段配置还在。开发说是AO表没备份。可我明明用了mysqldump --all-databases,为什么还会丢?怎么批量确认哪些AO表需要备份?
AO表是Active Objects框架生成的,每个插件可能创建多张带AO_XXX_前缀的表,前缀中的XXX是插件哈希。我遇到过最头疼的情况:mysqldump默认使用--complete-insert,但AO表通常有外键自引,导致恢复时报错跳过部分表。
具体细节: - 备份前先列出所有AO表:SHOW TABLES LIKE 'AO_%'; 在MySQL中或 \dt public.ao_* 在PG中。
- 用参数导出:
mysqldump --single-transaction --quick --add-drop-table --tables $(mysql -e "SHOW TABLES LIKE 'AO_%'" -N | tr '\ ' ' ' ) > ao_tables.sql- 恢复时一定要先停Jira,清空AO表再导入,否则插件会检测到旧元数据而拒绝加载。
我的检查方法: 恢复后进入Jira管理→系统→活跃对象(Active Objects),如果某插件显示“数据未加载”,说明对应的AO表缺失。
独特视角: 不要只盯着AO表,还要备份pluginstate表(存储插件启用/禁用状态),忘记它会导致恢复后所有插件被禁用,我为此亏了2天调试时间。对你有用的决策: 迁移前先用脚本生成AO表列表,对比源库和目标库的数量差异。
推荐使用Atlassian官方推荐的--extended-insert参数避免外键损坏。
4. 迁移后如何快速验证这三张表恢复成功了?
我按网上教程备份了OS_PROPERTYENTRY、jiraschema相关表和AO表,恢复后Jira能启动,但总担心哪里漏了。有没有15分钟内能完成的验证清单?最好能用SQL直接跑一下看结果。
我在为一家金融客户做Jira迁移时,甲方要求出具迁移验证报告。我总结了一套“三表验证法”,每次迁移后必做,发现过至少3次隐藏问题。
验证清单(按优先级排序): 1. OS_PROPERTYENTRY完整性 - SQL: `
SELECT COUNT(*) FROM propertyentry WHERE property_key LIKE 'jira.%';
- 期望:大于200条(Jira 9.x默认约230+) - 同时检查jira.license是否包含®等特殊字符(编码问题导致License失效) 2. jiraschema底层表一致性 - SQL: SELECT count(*) FROM project_scheme WHERE scheme_id NOT IN (SELECT id FROM project);
- 期望:结果0(无孤立scheme) - 再查:
SELECT count(*) FROM permission_scheme;
应与源库一致(我记录源库数量为15) 3. AO表数量对比 - 脚本生成两个环境下AO表数量: ``bash mysql -h源 -e "
SELECT count(*) FROM information_schema.tables WHERE table_schema='jiradb' AND table_name LIKE 'AO_%';
" > 源数.txt mysql -h目标 -e "同上" > 目标数.txt - 数量差异超过5%就要怀疑某个插件数据丢失 我的真实案例: 一次迁移后AO表数量少了12张,用上述脚本定位后发现是AO_3D73B_(某工作流插件)缺失。
追溯原因是目标库MySQL版本高了0.1,导致导入时该表的自增列语法不兼容,mysqldump静默跳过了。专家判断: 验证不只是跑SQL,还要做功能抽查,登录一个项目、创建一个Issue、查看自定义字段、发送测试邮件。建议把验证步骤做成脚本,每次迁移后自动化运行,节省大量复查时间。
核心关键词
文章包含AI辅助创作:jira迁移前先备份这3张表,发布者:fiy,转载请注明出处:https://worktile.com/kb/p/3975656
微信扫一扫
支付宝扫一扫
读者评论
做过5次Jira迁移的老运维表示,这篇文章说到了点子上。OS_PROPERTYENTRY那张表真的是最容易被忽略的隐形杀手,我之前也因为漏掉它导致许可证报错,折腾了一整晚。AO_*表也是个大坑,不同插件生成的表名乱七八糟,手动挑简直要命。强烈建议把这三类表的单独导出加入标准SOP,别盲目相信全库dump。
作为项目负责人,读完最大的收获不是技术细节,而是对迁移风险的认知。作者用两次真实故障案例把“省几分钟恢复时间却花几小时排查”的坑讲透了,这种决策层面的视角比单纯列命令更有价值。以后团队做Jira迁移,我肯定会要求他们按文章里的检查清单先做预演。
太真实了,我们公司年初从Jira Server迁移到云版,就是因为没备份AO_*表,所有自定义字段面板全部丢失,30多人的研发团队用了两周才用手工重建。要是早看到这篇文章,至少能提前导出那几张表的SQL。现在已经截图保存,下次升级前对照执行。
正在考虑要不要从Jira迁移到其他工具,这篇文章给了我很大的信心。作者提到的三类表涵盖了系统配置、插件数据和业务骨架,逻辑上确实能覆盖90%的关键数据。不过有一点想请教:如果迁移目标不是Jira本身,而是PingCode这种替代品,这三张表的迁移方式会不会有差异?比如AO_*表的数据是否能无缝对接过去?
看完了,写得很实在,不像那些只会粘贴官方文档的搬运文。最欣赏的是作者把每个表的实际影响场景拆解得很清楚,还放了故障复盘数据,说服力很强。唯一想补充的是:备份完成后的完整性校验也值得单独写一节,比如通过对比行数、用脚本检查关键键值是否存在,否则备份文件本身也可能损坏。