1. 项目背景与挑战
去年接手了一个棘手的任务:将公司内网环境中Confluence 9上积累的20GB核心知识库完整迁移到Wiki.js平台。这个看似简单的数据搬运工作,在实际操作中遇到了诸多技术挑战。不同于公有云环境,纯内网部署的限制让每个环节都需要特殊处理。
核心难点在于:
- 内网环境完全隔离,无法使用任何云服务或在线工具
- Confluence 9的备份机制与新版差异较大,常规导出方法失效
- Wiki.js使用完全不同的存储架构(MySQL+Git vs Confluence的XML数据库)
- 20GB数据量导致迁移过程中的性能问题和校验复杂度倍增
2. 迁移方案设计
2.1 技术路线选择
经过多轮评估,最终确定的迁移路径如下:
- 数据提取层:使用Confluence REST API + 自定义Python脚本
- 转换层:开发专门的格式转换中间件
- 存储层:搭建内网Git仓库作为中间存储
- 导入层:Wiki.js的Git同步机制+MySQL批量导入
关键决策:放弃使用Confluence原生备份功能,因为其生成的XML格式与Wiki.js的Markdown体系兼容性极差,且无法保留版本历史。
2.2 环境准备清单
| 组件 | 版本要求 | 用途说明 |
|---|---|---|
| Python | 3.8+ | 数据提取和转换脚本运行环境 |
| GitLab CE | 14.9 | 内网Git仓库托管转换后的文档 |
| MySQL | 8.0.23 | Wiki.js的数据库后端 |
| Node.js | 16.x | Wiki.js运行环境 |
| Nginx | 1.20+ | 反向代理和负载均衡 |
3. 详细实施步骤
3.1 Confluence数据提取
编写Python脚本通过Confluence REST API获取数据:
import requests from requests.auth import HTTPBasicAuth def get_space_pages(confluence_url, space_key, auth): start = 0 limit = 50 all_pages = [] while True: url = f"{confluence_url}/rest/api/content?spaceKey={space_key}&start={start}&limit={limit}" response = requests.get(url, auth=auth) data = response.json() if not data['results']: break all_pages.extend(data['results']) start += limit return all_pages关键参数说明:
limit=50:单次请求最大记录数,内网环境下建议不超过100- 分页获取:避免单次请求数据量过大导致超时
- 基础认证:内网环境使用HTTP Basic Auth更安全
3.2 格式转换处理
Confluence存储的HTML需要转换为Markdown,我们采用以下转换规则:
- 表格处理:使用
html2markdown库转换后,手动校正合并单元格 - 附件迁移:保持原始文件哈希值不变,仅修改引用路径
- 版本历史:将Confluence的版本信息转换为Git commit历史
典型转换代码示例:
from html2markdown import convert def convert_to_markdown(html_content): # 预处理Confluence特殊标签 html_content = html_content.replace('<ac:structured-macro', '<div') html_content = html_content.replace('</ac:structured-macro>', '</div>') markdown = convert(html_content) return markdown3.3 Git仓库管理策略
在内网GitLab上创建符合Wiki.js要求的仓库结构:
/wiki-root ├── documents │ ├── page1.md │ └── subdir │ └── page2.md └── wiki.js └── config.yml目录规范:
- 保持与Confluence相同的空间/页面层级
- 文件名使用URL-safe编码
- 每个.md文件头部添加YAML front matter存储元数据
3.4 MySQL数据库优化
针对20GB数据量特别调整的MySQL配置:
[mysqld] innodb_buffer_pool_size = 8G innodb_log_file_size = 2G max_allowed_packet = 256M transaction_isolation = READ-COMMITTED优化要点:
- 缓冲池设置为可用内存的70%
- 增大日志文件避免频繁flush
- 调整事务隔离级别提高并发性能
4. 关键问题与解决方案
4.1 大附件处理难题
现象: 超过100MB的附件在Git推送时失败
解决方案:
- 启用Git LFS管理大文件
- 修改GitLab配置:
# /etc/gitlab/gitlab.rb gitlab_rails['git_max_size'] = 10240 # 10GB gitlab_rails['git_timeout'] = 600 # 10分钟4.2 特殊字符转换异常
典型案例: Confluence的{code}宏转换为Markdown代码块时格式错乱
修复方法: 开发自定义转换规则:
def convert_code_blocks(text): pattern = r'\{code(?::(.*?))?\}(.*?)\{code\}' replacement = r'```\1\n\2\n```' return re.sub(pattern, replacement, text, flags=re.DOTALL)4.3 版本历史丢失
解决路径:
- 从Confluence审计日志提取编辑记录
- 转换为Git commit时间线:
#!/bin/bash while read -r date author email message; do GIT_AUTHOR_DATE="$date" GIT_COMMITTER_DATE="$date" \ git commit --author="$author <$email>" -m "$message" done < history.log5. 迁移后验证
5.1 完整性检查
开发校验脚本验证三项核心指标:
- 页面数量匹配度
- 附件MD5校验和
- 版本历史覆盖率
def verify_migration(source_conf, target_wiki): src_count = get_confluence_page_count(source_conf) dst_count = get_wiki_page_count(target_wiki) assert src_count == dst_count, f"Page count mismatch: {src_count} vs {dst_count}" # 附件校验逻辑... # 版本历史校验逻辑...5.2 性能基准测试
模拟100并发请求的响应时间对比:
| 操作类型 | Confluence(ms) | Wiki.js(ms) |
|---|---|---|
| 页面加载 | 1200 | 400 |
| 搜索查询 | 2500 | 800 |
| 保存编辑 | 1800 | 300 |
6. 经验总结
- 预处理很重要:迁移前清理Confluence中废弃的草稿和垃圾页面,使数据量减少15%
- 分批处理:按空间分批迁移,每批控制在500个页面以内
- 版本控制:每个迁移批次打上Git tag便于回滚
- 监控指标:实时记录CPU/内存/网络使用情况,我们发现的异常模式:
- MySQL连接数突增 → 调整连接池配置
- Git推送速度下降 → 优化内网交换机配置
整个迁移过程耗时3周,最终实现:
- 零数据丢失
- 完整保留10年版本历史
- 搜索性能提升300%
- 存储空间节省40%(得益于Markdown的简洁性)
这次经历让我深刻体会到:在内网环境下进行大规模数据迁移,提前设计验证方案比技术实现更重要。每个环节都需要考虑回退机制,我们准备了5套应急方案,最终用上了其中3套。