当前位置: 首页 > news >正文

第五难: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[] List<Integer> 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文件格式清晰明了
http://www.jsqmd.com/news/1101090/

相关文章:

  • ESXi 免费版有官方技术支持吗?订阅授权支持规则说明
  • SENAITE LIMS:现代化实验室信息管理系统的架构解析与实施指南
  • 别再死记硬背公式了!用Python可视化理解拉梅系数与正交坐标系
  • 别再傻傻分不清!一文搞懂Chiplet、SiP、SoC和MCM到底有啥区别(附AMD实例)
  • 灯塔工厂的AI底座:从单点智能到工厂核心操作系统的演进
  • 3步解锁百度网盘30倍下载速度:从限速到飞驰的实战指南
  • 别再问‘服务器能扛多少QPS’了!从4核8G的压测数据,聊聊真实业务场景下的性能估算
  • 企业级考研互助交流平台管理系统源码|SpringBoot+Vue+MyBatis架构+MySQL数据库【完整版】
  • SAP采购申请报表开发避坑指南:EBAN/EBKN表关联与审批状态判断的实战细节
  • 从Wireshark抓包看CURLOPT_POSTFIELDSIZE:为什么你设置的包大小和抓到的TCP包不一样?
  • 连享会课程分享
  • 3个技巧快速掌握多显示器亮度调节神器
  • 112G AI 服务器高速线束自动化生产线定制指南 非标线束整线方案参考
  • Axure RP中文界面终极指南:3分钟搞定完整汉化教程
  • 终极指南:使用QrazyBox免费修复损坏二维码
  • 别再混淆了!嵌入式开发中的TCM、ITCM、DTCM到底怎么用?(以Cortex-M为例)
  • 告别Anchor框!用HRNet+CenterNet搭建YOLC,实测VisDrone小目标检测AP提升5%
  • GSAP 高级动画技巧:构建丝滑流畅的页面动效编排
  • 多通道高速采集系统的“最后一步”:零拷贝DMA设计——避免CPU卡死、数据错位的工程实践
  • 空洞骑士模组管理器Scarab:跨平台一键安装的智能解决方案
  • 别再直接积分了!用MPU6050陀螺仪数据算姿态角,为什么你的无人机飞机会‘乱飘’?
  • AI合规高阶:AI跨境合规的难点与解决方案
  • 逆向实战:用Python一步步还原新版a_bogus算法(附完整日志分析)
  • 别再死记硬背公式了!用Python可视化理解拉梅系数在柱坐标/球坐标下的应用
  • 从音频到视频再到CT扫描:Conv1d, 2d, 3d在真实项目里到底怎么选?
  • 5步掌握免费NCM音乐转换:NcmppGui极速解密指南
  • 新手吉他选购指南,2026零基础500-3000元吉他实测推荐
  • 从怀疑到信任,我为什么最终选择一直留在 SaviCoin 交易所?
  • 制造企业的合同困局:为何一份采购合同要等两周才能签完
  • 消息队列中间件详解:RabbitMQ 与 ActiveMQ 从入门到运维