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迁移以前最大的顾虑就是:

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

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

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

当然,它不是万能方案。

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

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

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

相关推荐
Vec‑Jie1 小时前
MerchantOps-KBQA 实践(十四):本地运行、性能限制与可验证的交付边界
人工智能·后端·python·flask
qo_tn1 小时前
微信机器人-webhook技术文档_02-部署前准备与环境规划
后端
雪隐1 小时前
WPF + MVVM 实战系列02-告别 INPC 手写时代,做个体面的现代 WPF 人
前端·后端·c#
霸道流氓气质1 小时前
SpringBoot中使用OAuth2 认证与 JWT Token — 概念、原理与实践
java·spring boot·后端
dogstarhuang1 小时前
Kimi K3 本地部署实战:从 1.56TB 权重到推理服务的完整成本分析
java·人工智能·后端·ai·开源·接口·程序员创富
星栈1 小时前
我以为 TS7.0 只是换个版本号,结果编译快了 9 倍,也踩了 5 个坑
后端·typescript·node.js
Csvn1 小时前
📊 SQL 入门 Day 15:日期与字符串函数 — SQL 中的数据处理瑞士军刀
后端·sql
用户84298142418101 小时前
JS代码压缩实测:可减小体积、提高执行效率!
前端·javascript·后端