第五难:MongoDB到PostgreSQL的类型转换

📅 2026/7/1 7:40:27
第五难:MongoDB到PostgreSQL的类型转换
问题MongoDB和PostgreSQL的数据类型完全不兼容MongoDBPostgreSQL问题ObjectId无对应类型主键转换BSON对象JSONB嵌套结构数组Array类型声明解决方案在配置表的扩展字段定义类型映射{ mongoCollection: user_profile, pgTable: user_profile, fieldMapping: { _id: id, preferences: preferences, tags: tags }, typeMapping: { _id: OBJECTID_TO_VARCHAR, preferences: JSONB, tags: INTEGER_ARRAY } }类型转换代码private String convertValue(Object value, String typeRule) { if (value null) return NULL; switch (typeRule) { case JSONB: // {name: test} → {name:test}::jsonb String json toJsonString(value); return escapeSql(json) ::jsonb; case INTEGER_ARRAY: // [1,2,3] → ARRAY[1,2,3]::INTEGER[] ListInteger list (List) value; return ARRAY[ String.join(,, list) ]::INTEGER[]; case OBJECTID_TO_VARCHAR: // ObjectId(507f...) → 507f... return value.toString() ; default: return convertDefault(value); } }复盘一个月完成迁移的关键整体架构塔外-塔内双链路┌──────────── 塔外系统 (Outer) ────────────┐│ ││ ① API触发同步 ││ ② 查询配置表 → 拆分公司级/店铺级配置 ││ ③ 构建MQ消息 → 投递RocketMQ ││ ④ MQ Consumer ││ ├─ SHOW CREATE TABLE 获取表结构 ││ ├─ 流式读取源数据库 ││ ├─ 生成 DELETE INSERT SQL ││ ├─ 分号替换为特殊符号 ││ └─ 上传到 OSS │└───────────────────────────────────────────┘││ OSS中转↓┌──────────── 塔内系统 (Inner) ────────────┐│ ││ ⑤ 定时任务 / 手动触发 ││ ⑥ 扫描OSS目录 → 获取待处理SQL文件列表 ││ ⑦ 流式下载SQL文件 → 逐行读取 ││ ├─ 特殊符号还原为分号 ││ ├─ 批量执行(1000条/批) ││ └─ setAutoCommit(true) 防止事务过大 ││ ⑧ 执行成功 → 立即删除OSS文件 │└───────────────────────────────────────────┘核心亮点总结技术点传统方案本方案效果表结构获取手写100个MapperSHOW CREATE TABLE动态解析零硬编码支持任意表SQL分隔符用;判断结束特殊符号;#END#支持数据含分号、换行符同步策略全量同步or硬编码配置表占位符灵活配置4种策略大数据量处理一次性加载(OOM)流式读取临时文件常量级内存50W行稳定扩展性新增表需改代码只需加配置秒级上线新表同步做对的3件事1. 从工具中偷师学艺Navicat的导入/导出功能启发了整体方案SHOW CREATE TABLE是突破口2. 把复杂逻辑放在塔外塔内只负责执行SQL逻辑简单塔外可以随意调试、优化3. 配置驱动而非代码驱动新增表只需加配置不改代码。后续维护成本趋近于0最终效果指标数据迁移表数量200张含后续新增最大单表数据1000万行首次全量同步10-30分钟日常增量同步公司级表约30秒店铺级表约1分钟内存占用稳定在200MB左右OOM次数0连续运行3个月工期25天提前5天完成写在最后以上便是我这次迁移实战的全部分享。绝非标准答案但希望能为你带来一丝灵感。这次迁移让我深刻体会到好的架构不是设计出来的而是从实际问题中偷出来的。当你面对技术难题时不妨问自己有没有现成的工具已经解决了类似问题不要重复造轮子Navicat数据库/框架本身提供了什么能力SHOW CREATE TABLE、setFetchSize能否用配置代替硬编码配置表占位符感谢那些默默扛下所有的技术细节SHOW CREATE TABLE—— 你扛下了表结构解析的苦活stmt.setFetchSize(Integer.MIN_VALUE)—— 你默默守护了内存安全;#END#—— 你可能是全网最诡异但最实用的分隔符RocketMQ的TAG过滤—— 你让消息路由变得优雅CompletableFuture—— 你让塔内并发处理成为可能System.lineSeparator()—— 你让SQL文件格式清晰明了