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迁移以前最大的顾虑就是:
数据库换了,代码是不是也要跟着推倒重来?
这次项目最大的感受是,兼容方案真正解决的不是"能不能存数据",而是降低迁移成本。
对于一些已经运行多年、代码复杂、业务不能停的系统来说,协议兼容路线确实提供了一种更现实的选择。
当然,它不是万能方案。
任何数据库迁移,都应该先做兼容性验证,再决定技术路线。
但至少这次项目让我少走了一条弯路:
不用为了换数据库,把整个业务系统重新写一遍。