MongoDB迁移不想重写代码?一次国产数据库替换踩坑记录

MongoDB迁移不想重写代码?一次国产数据库替换踩坑记录

这个项目最开始,我其实是不太想接的

去年参与过一个比较特殊的数据库迁移项目。

客户是一套政务电子证照系统,原来的数据库是MongoDB,里面存放的是大量证照类数据,总量超过2TB。

系统运行时间比较久,业务代码也经历过多轮迭代。

简单看了一下代码,感觉问题不小。

Java服务里各种Mongo查询对象、聚合条件、嵌套文档处理逻辑很多,还有一些历史遗留代码。按照传统迁移方式,如果换成普通关系型数据库,大概率不是简单的数据迁移,而是连应用层一起改。

客户当时给了几个要求:

第一,历史数据不能丢。

第二,业务不能停太久。

第三,尽可能不要修改已有代码。

说实话,这几个条件放在一起,压力还是挺大的。

因为MongoDB和传统数据库最大的区别,不在于存储数据,而在于使用方式。

Mongo里面一个Document可以直接嵌套对象、数组。

业务代码里很多地方也是按照文档结构直接读取。

如果全部转换成关系模型,需要重新设计表结构,再改查询逻辑。

按照之前做过的一些项目经验,这种工作量至少几个月起步。

后来换了一条思路:先保证应用跑起来

项目后面采用的是金仓数据库MongoDB兼容方案。

最开始我其实也有疑问。

因为以前接触过一些"兼容方案",大部分思路都是增加一层转换。

比如:

MongoDB请求过来 → 转换成SQL → 执行 → 返回Mongo格式数据。

这种方式理论上可以实现兼容,但实际使用容易出现问题。

简单查询可能没问题,一旦涉及复杂聚合、嵌套结构,转换逻辑就容易失效。

所以第一次测试的时候,我重点验证的不是简单CRUD,而是业务里面真实存在的查询场景。

比如原系统里面大量使用这种嵌套结构:

json 复制代码
{
    "name":"张三",
    "age":30,
    "profile":{
        "city":"北京",
        "tags":[
            "admin",
            "vip"
        ]
    }
}

对应Java代码基本还是MongoDB原来的写法:

java 复制代码
Query query = new Query();

query.addCriteria(
    Criteria.where("profile.city")
    .is("北京")
);

mongoTemplate.find(query, User.class);

迁移测试时,应用层基本没有调整。

这一点对于老系统来说其实非常重要。

因为数据库迁移项目里面,真正耗时间的往往不是导数据,而是重新验证业务逻辑。

协议兼容,解决的是开发侧成本

很多人理解数据库兼容,会认为就是支持几个SQL语法。

其实不是。

对于MongoDB应用来说,更重要的是客户端连接方式。

MongoDB客户端和数据库之间有自己的通信协议。

如果数据库能够兼容这个协议,那么原来的MongoDB驱动、开发框架,包括一些管理工具,都可以继续使用。

例如:

python 复制代码
from pymongo import MongoClient


client = MongoClient(
    "mongodb://system:123456@127.0.0.1:27017/testdb"
)

db = client["testdb"]

collection = db["users"]


collection.insert_one({
    "name":"张三",
    "age":30,
    "profile":{
        "city":"北京"
    }
})


result = collection.find({
    "profile.city":"北京"
})


for item in result:
    print(item)

这类代码不需要重新改成SQL。

对于已经上线多年、业务复杂的系统来说,这一点价值很明显。

毕竟很多企业系统不是没有人维护,而是不敢大规模修改。

迁移之后,发现多模能力反而比较有优势

项目稳定以后,我们又开始考虑另外一个问题:

既然已经换数据库了,是不是可以顺便优化一下原来的架构?

以前的系统里面:

MongoDB存文档。

关系数据库存业务数据。

两个系统之间通过接口同步。

这种架构在早期开发阶段很方便,但是运行几年后,数据关联越来越复杂。

很多查询需要:

先查业务库。

再查Mongo。

最后应用层拼数据。

金仓这类多模数据库方案的一个优势,就是可以同时处理关系数据和文档数据。

例如:

sql 复制代码
CREATE TABLE users(
    id SERIAL PRIMARY KEY,
    user_info JSONB
);


INSERT INTO users(user_info)
VALUES(
'{
"name":"张三",
"city":"北京"
}'
);


SELECT
user_info->>'name',
user_info->>'city'
FROM users;

对于一些半结构化数据,不一定需要强行拆成几十张表。

这对于历史系统改造其实比较友好。

当然,也不是所有情况都适合直接迁移

这里也说几个实际情况。

第一,兼容不是百分百复制MongoDB。

如果你的系统大量使用MongoDB非常特殊的能力,比如复杂聚合、多阶段Pipeline、大规模分片,这些还是需要提前验证。

第二,迁移前一定要做业务测试。

数据库迁移最怕什么?

不是迁不过去。

而是迁过去以后,某个几年没人碰的功能突然异常。

所以我们的做法是:

先迁测试环境。

跑完整业务流程。

做数据校验。

最后再切生产。

第三,不要只看数据库。

很多团队做迁移时,只关注数据库本身。

实际上:

驱动版本。

连接池配置。

索引设计。

查询习惯。

这些都会影响最终效果。

最后总结一下

MongoDB迁移以前最大的顾虑就是:

数据库换了,代码是不是也要跟着推倒重来?

这次项目最大的感受是,兼容方案真正解决的不是"能不能存数据",而是降低迁移成本。

对于一些已经运行多年、代码复杂、业务不能停的系统来说,协议兼容路线确实提供了一种更现实的选择。

当然,它不是万能方案。

任何数据库迁移,都应该先做兼容性验证,再决定技术路线。

但至少这次项目让我少走了一条弯路:

不用为了换数据库,把整个业务系统重新写一遍。

相关推荐
++==18 分钟前
RESTful详解:核心思想、框架、与HTTP的区别,API的设计风格
后端·http·restful
IT_陈寒1 小时前
Vue的响应式更新有时候真的不听话
前端·人工智能·后端
向生2 小时前
Ubuntu Caddy 保姆级完整教程
后端
tntxia2 小时前
Docker权限的问题
后端
明月_清风2 小时前
看完这段关于"全插件化架构"的技术分析后,我整理了一份笔记
前端·后端
摇滚侠3 小时前
《SpringBoot 3:入门与应用实战》第 7 章 AOP 思想与实现 阅读笔记 14
spring boot·笔记·后端
她的男孩3 小时前
我把管理系统接给AI,它改条数据都要先问我
java·后端·架构
元界metalite4 小时前
SpringBoot分页接口怎么设计-pageSize不设上限会发生什么
后端
IT爱学堂5 小时前
Go开发疑难杂症终结者通关指南
后端
大勇前进5 小时前
从10分钟到10秒:一个真实慢查询的SQL Server优化全记录
后端