从烟囱到融合:一次MongoDB迁移引发的架构重构深思

文章目录



兼容 是对前人努力的尊重 是确保业务平稳过渡的基石 然而 这仅仅是故事的起点


说真的,我一开始根本没打算写这篇东西。上个月帮一个老朋友搞MongoDB迁移,折腾了大半个月,过程中把KES的MongoDB兼容版翻来覆去用了好几遍,踩了不少坑也想明白了不少事。

MongoDB原生协议兼容这个事,我得展开说说

当时我也不太信这个"零代码修改"的说法,因为之前见过太多号称兼容实际上各种不兼容的案例。但翻完KES MongoDB兼容版的产品文档和一些社区帖子之后,发现它确实是走了一条比较硬核的路------不是在应用层做适配,而是在数据库协议层直接兼容。

啥意思呢?你的Java应用原来用MongoDB Java Driver连接MongoDB,现在只要把连接地址从MongoDB服务器改成KES服务器,端口设成27017(KES监听这个端口来接收MongoDB协议请求),其他啥都不用动。Driver发出的所有insertOne、find、update这些命令,KES都能识别和处理。

java 复制代码
// 原来连MongoDB
MongoClient client = MongoClients.create(
    "mongodb://user:pass@mongo-host:27017/mydb"
);

// 迁移到KES,只改地址
MongoClient client = MongoClients.create(
    "mongodb://system:123456@kes-host:27017/mydb"
);

// 后续所有操作完全不变
MongoCollection<Document> collection = 
    client.getDatabase("mydb").getCollection("users");

// 插入文档
collection.insertOne(new Document("name", "张三")
    .append("age", 28)
    .append("tags", Arrays.asList("vip", "active")));

// 查询
FindIterable<Document> results = collection.find(
    Filters.eq("age", 28)
);

看到没?就是改个连接字符串的事。Spring Data MongoDB、PyMongo这些主流框架也都能直接用,因为KES兼容的是MongoDB Wire Protocol,而不是简单做了个API翻译层。

我当时跟朋友说,你可以先用 mongosh 连上去试试,感觉就跟连了个真的MongoDB一样:

javascript 复制代码
// 用mongosh连接KES的MongoDB兼容端口
mongosh "mongodb://system:123456@127.0.0.1:27017/mydb?directConnection=true&authMechanism=SCRAM-SHA-256"

// 创建集合
db.createCollection("orders")

// 插入文档
db.orders.insertOne({
    order_id: "ORD20260801001",
    customer: {
        name: "李四",
        phone: "138xxxx0001"
    },
    items: [
        {sku: "SKU001", qty: 2, price: 99.9},
        {sku: "SKU002", qty: 1, price: 199.0}
    ],
    status: "pending",
    created_at: new Date()
})

// 查询
db.orders.find({"customer.name": "李四"})

// 聚合查询,统计每个客户的订单数
db.orders.aggregate([
    {$group: {_id: "$customer.name", orderCount: {$sum: 1}}},
    {$sort: {orderCount: -1}}
])

朋友试了之后说"卧槽真的能用",说实话我也挺惊讶的。因为KES对MongoDB常用命令的支持率非常高,查询和写入类命令100%覆盖,更新操作符100%覆盖,聚合管道操作符98.82%。只有一些不太常用的管理类命令(比如角色管理那块)没完全兼容,但KES本身有自己的一套权限管理机制,可以通过KES的管理工具来做。

然后聊到迁移具体怎么搞

零代码修改是应用层的事,但数据库本身的迁移还是得做点工作的。我把KES文档里提到的步骤捋了一遍,大致是这么个流程。

首先是初始化KES实例,得指定兼容模式:

bash 复制代码
# 初始化KES,指定兼容模式
# -m 参数指定兼容模式,具体模式值参考官方部署手册
# -U 指定超级用户
initdb -U system -D /data/kes_data

这里插一句,KES支持多种兼容模式,不同模式下语法习惯和默认行为有差异。MongoDB兼容功能是在内核层面实现的,通过加载插件来启用。

然后改配置文件 kingbase.conf,开启MongoDB协议兼容:

ini 复制代码
# kingbase.conf 中添加或修改以下参数

# 开启协议兼容
enable_protocol_compat = on

# MongoDB兼容监听端口,默认就用27017
extension_protocol_port = 27017

# BSON格式使用EJSON
documentdb_core.bsonUseEJson = on

# 在shared_preload_libraries中追加以下三个模块
shared_preload_libraries = 'kdb_cron, kdb_documentdb_core, kdb_documentdb'

这几个参数逐个说下。enable_protocol_compat是总开关,打开后KES才能处理非SQL协议连接。extension_protocol_port指定MongoDB协议监听端口,设27017是为了跟原生MongoDB一致,迁移时连接串只改IP就行。bsonUseEJson控制BSON序列化方式,建议开启。shared_preload_libraries里那两个模块是KES实现MongoDB兼容的核心组件,必须在启动时加载。

配置改完之后重启数据库,然后登录进去创建插件:

bash 复制代码
# 用ksql连接数据库
ksql -U system -p 54321 mydb
sql 复制代码
-- 创建MongoDB兼容插件,cascade会自动安装依赖组件
CREATE EXTENSION documentdb CASCADE;

-- 给system用户设置密码,MongoDB客户端连接时需要
ALTER USER system WITH PASSWORD '123456';

执行完这一步,KES就处于MongoDB兼容模式了。此时你可以用任何MongoDB客户端工具来连接它,mongosh、MongoDB Compass、Navicat都行,选择MongoDB数据源直接连。

朋友问数据怎么搬。KES有配套的KDTS做存量数据迁移,图形化工具,配好源端MongoDB和目标端KES连接信息就行。数据量大或对停机时间有要求的话,还有KFS做实时增量同步,边搬边追增量,最后切换只需要很短停机窗口。

bash 复制代码
# KDTS命令行示例(具体参数根据版本调整)
# 全量迁移
kdts --mode full \
     --source "mongodb://source-host:27017" \
     --target "kingbase://kes-host:27017" \
     --database mydb

# 增量同步(不停机迁移用)
kfs --mode incremental \
    --source "mongodb://source-host:27017" \
    --target "kingbase://kes-host:27017" \
    --database mydb

不过KDTS和KFS具体用法还是得看官方部署手册,我这里只给个概念。

性能这块,我得说实话

我知道很多人关心迁移完性能会怎样。老实说,KES在纯文档操作的性能上跟原生MongoDB比是有差距的。从产品文档里给的测试数据来看,1万条数据规模下,INSERT操作KES是144毫秒,MongoDB是100毫秒;10万条数据时,KES的UPDATE是543毫秒,MongoDB是328毫秒。到了百万级数据,差距更明显一些,UPDATE操作KES 6413毫秒,MongoDB 3083毫秒。

复制代码
数据量:1万条
操作        MongoDB    KES
INSERT      100ms      144ms
UPDATE      35ms       52ms
SELECT(全表) 24ms       38ms
SELECT(标量) 4ms        6ms

数据量:100万条
操作        MongoDB    KES
INSERT      2275ms     3498ms
UPDATE      3083ms     6413ms
SELECT(全表) 2087ms     3320ms
SELECT(标量) 174ms      270ms

差距是客观存在的。但这里面有个核心问题------你是拿KES跟MongoDB单独比文档操作性能,可KES的真正价值在于融合。三套系统的总成本------硬件、授权费、运维人力、数据同步开销------跟一套KES比,哪个高?我朋友算过一笔账,光运维人力一年省两个人就够覆盖性能差距带来的硬件投入了。

再说安全这块,MongoDB默认认证弱、传输加密要手动配、没有透明数据加密、审计得靠第三方插件。信创和等保2.0背景下这些是硬伤。KES有细粒度RBAC、国密SM2/SM3双向认证、SSL/TLS、TDE透明数据加密、内建审计,这套纵深防御体系是MongoDB给不了的。

还有一点,KES的文档操作性能虽然慢一些但完全够用。标量查询百万级数据270毫秒,对绝大多数业务可接受。而且KES还有SQL接口操作文档数据,有些复杂分析用SQL写比MongoDB聚合管道方便太多。

SQL操作文档数据这个能力,被严重低估了

说到SQL操作文档数据,我觉得这个功能被很多人忽略了。KES除了支持MongoDB原生协议访问文档数据之外,还支持直接用SQL来查。这意味着什么?意味着你可以在一个SQL语句里同时操作关系型数据和文档数据。

举个例子,你有个用户表(关系型)和一个订单集合(文档型),原来分属MySQL和MongoDB两个库,想要关联查询得写ETL先同步数据再跑分析。现在都在KES一个库里了:

sql 复制代码
-- users是关系表,orders是文档集合对应的表
-- 用SQL直接做关联查询
SELECT u.username, u.phone, o.order_info
FROM users u
JOIN orders o ON u.user_id = o.order_info->>'user_id'::int
WHERE o.order_info->>'status' = 'pending'
  AND u.register_time >= '2026-01-01';

-- 也可以直接用SQL插入文档数据
INSERT INTO orders (order_info) 
VALUES ('{"order_id": "ORD001", "customer": "张三", "amount": 299.0}');

-- JSONB字段上的查询操作
SELECT order_info->>'customer' AS customer_name,
       (order_info->>'amount')::numeric AS amount
FROM orders
WHERE order_info @> '{"status": "completed"}'
ORDER BY amount DESC
LIMIT 10;
javascript 复制代码
// 同样的查询用MongoDB语法也能做
db.orders.find(
    {"status": "completed"}
).sort({"amount": -1}).limit(10)

你看,同一份数据,你可以用MongoDB驱动访问也可以用SQL访问,取决于你的场景需要。开发新功能的时候用SQL多方便啊,JDBC直接连上就能写,不用学MongoDB的查询语法。老代码不动,继续用MongoDB驱动跑。这种灵活性是真的香。

你团队里那些只会写SQL的后端,以前碰MongoDB的数据就抓瞎,现在一条SQL就能搞定跨数据模型的关联查询。

多集群架构这个事也值得聊聊

KES的融合架构里还有一个概念叫"集中分布一体化",说白了就是它支持多种部署模式------单机、主备、分布式集群,可以根据你的可用性需求和成本预算来选。

朋友原来MongoDB用Replica Set三节点,MySQL主从一备,Redis三主三从cluster。三套高可用方案、三套故障切换逻辑,出故障时排障顺序都能搞晕人。

KES的多集群架构思路是:同一套数据库,先单机做开发测试,再切主备上生产,业务量上来扩到分布式集群。底层的数据模型和SQL语法都不变,只是部署形态变了。

复制代码
单机模式:开发测试用
    ↓
主备模式:小规模生产,满足基本高可用
    ↓  
分布式集群:大规模生产,水平扩展+高可用

而且KES分布式集群用MPP架构,数据分片存在不同节点上,查询时各节点并行处理。后面要做跨业务数据分析,不需要额外搭数据仓库,直接在KES上跑就行。

朋友听完这些之后说了一句话让我印象很深,他说"早知道有这种融合方案,我当初就不该搞那么多套数据库了"。说实话我也是这种感觉,很多时候技术选型上的"分别部署"不是有意为之,而是业务发展过程中一步步加出来的,等问题积累了才发现收不了场。

关于融合数据库架构的一些碎碎念和文献思考

聊到这块我想多说几句。数据库融合架构这个概念,其实不是KES首创的,但不同厂商走的技术路线差异很大,理解这些差异对你做技术选型挺重要的。

你去看早期做多模数据库的尝试,大概2015年前后吧,当时业界讨论的热点是"NewSQL能不能取代NoSQL"。一批人认为关系型数据库通过扩展JSON类型就能覆盖文档数据库的场景,另一批人觉得NoSQL的灵活schema和水平扩展能力是关系型数据库无法替代的。这个争论持续了好几年,最后的结果是两边都没完全取代对方,反而催生了一种新的思路------在关系型内核上原生支持多种数据模型。

不过我也得说句公道话,KES的多模融合方案目前也不是完全没有短板。比如在超大规模向量检索场景下(十亿级以上向量),专业向量数据库的检索性能还是更有优势的。再比如文档模型那边,一些MongoDB的高级特性像查询计划缓存、角色管理命令这些还没完全兼容。这些gap是否影响你的业务,得自己评估。

我个人觉得,融合数据库架构这条路方向是对的。与其追求单一场景的极致性能然后忍受多系统的运维痛苦,不如在"够用"的性能水平上把技术栈收敛了。尤其对于信创背景下的国产化替代来说,你本来就要换数据库了,与其一个MongoDB换成另一个MongoDB、一个MySQL换成另一个MySQL,不如一步到位选个能融合的,把长期的技术债一起清了。

回到那次迁移本身

最后说说我朋友那边的结果吧。经过大半个月的折腾,最终方案是核心交易数据保持在KES的关系型存储里,MongoDB里的用户画像和商品动态数据迁移到了KES的文档存储,Redis缓存暂时保留但后续也计划迁移到KES(KES本身也有缓存能力)。GIS相关的业务直接用了KES内置的KGIS组件。

迁移之后最直观的感受是数据一致性问题彻底解决了。因为所有数据都在一个库里,跨表事务由ACID保证,再也不用依赖ETL和补偿逻辑了。运维那边反馈说监控面板从一个铺满四个屏幕的大屏变成了一块屏,看监控的时候终于不用来回转头了(这是原话,我笑了半天)。

应用代码那边,MongoDB相关的代码几乎没改,就是连接字符串换了一下。倒是有些原来用ETL做跨库分析的任务,现在直接改成了SQL关联查询,代码量少了一大截。朋友说光把ETL相关的代码删掉就删了两千多行,删的时候特别爽。

当然了过程中也有踩坑的地方。比如MongoDB的一些聚合管道操作符KES虽然支持但行为细节有差异,$lookup在处理大数据量的时候内存占用比原生MongoDB高一些,需要调整work_mem参数。还有mongosh连接的时候认证机制要指定SCRAM-SHA-256,不指定的话有些版本会默认用SCRAM-SHA-1导致认证失败。

相关推荐
Asize1 小时前
无状态 Stateless:大模型为什么记不住你是谁
javascript·人工智能·架构
echoVic1 小时前
Agent 的会话为什么是一棵树,而不是一串消息
架构·agent
echoVic2 小时前
Plan 模式为什么不能只是一个权限开关
架构·agent
echoVic2 小时前
运行中的修改,到底什么时候生效
架构·agent
echoVic2 小时前
为什么 Agent 的每个请求,都要先拍一张快照
架构·agent
烬羽2 小时前
组件拆了,逻辑没拆——自定义 Hook 才是 React 业务逻辑的正当归属
react.js·架构·typescript
小月土星2 小时前
自定义业务 Hook:从零解剖一个 React + TypeScript Todo 应用:用金字塔思维看透前端架构
react.js·架构·前端框架
AI 小老六2 小时前
Brainstorming 与 grill-me 在 AI 产品设计和工程决策中的分工边界
人工智能·ai·架构·创业创新
重庆小透明4 小时前
深入探寻微服务【第一篇微服务的坏】
微服务·云原生·架构