做 MongoDB 迁移之前,先看看这套“用 SQL 玩文档“的路子行不行
做 MongoDB 迁移之前,先看看这套"用 SQL 玩文档"的路子行不行

上个月帮朋友评估一个数据库选型的事。他们有个 MongoDB 集群要换,一问原因,倒不是嫌 MongoDB 不好用,主要是合规那边过不去,加上运维两套技术栈实在有点费人。聊到 MongoDB 迁移这个话题,我发现很多人第一反应是"文档库换文档库",其实还有另一条路:换成一个能用 SQL 处理文档的关系库。
这篇就聊聊金仓的 KES MongoDB 兼容版,重点讲三件事:它怎么做到零代码平替、基于 SQL 的文档开发到底怎么用、还有迁移的时候具体要动哪些配置。
一、这东西本质上是个双协议数据库
先把定位说清楚,不然后面容易绕。
KES MongoDB 兼容版不是另起炉灶做的一个新产品,它是 KES 上的一个能力扩展。同一个数据库实例,同时开两个端口:27017 给 MongoDB 协议用,54321 给 KES 自己的协议用。
也就是说,你的老应用继续用 MongoDB 驱动连 27017,新应用用 KES 驱动连 54321,两边看的是同一份数据。
架构大概长这样:
这里有个细节值得注意。语法层是分开的,MongoDB 的请求走 MongoDB-parser,SQL 请求走 Oracle-parser 或者 PGSQL-parser,但到了存储层是共用的。这就是它能把关系数据和文档数据放在一起查的前提。
二、"零代码平替"是怎么实现的
官方给的说法是"仅需要修改驱动层的连接指向,应用零修改"。
听起来挺夸张,但拆开看逻辑是通的。因为它在协议层面做兼容,不是去做 API 转换。你的应用发出去的还是 MongoDB 的原生协议报文,KES 这边有个协议组件接住,解析完之后转成内部的执行计划。对应用来说,它并不知道对端已经换人了。
对象模型这一层也做了对应关系:
也就是说,一个 collection 在底层就是一张表,一个 document 就是一行。BSON 类型这块,是靠 KES 关系模型的自定义类型、自定义函数、自定义访问方法这几个能力拼出来的。
索引也做了对应。_id 字段会自动建 B-TREE 索引,你手动执行 db.products.createIndex({"item":1}) 的时候,底层建的是一个 RUM 索引。RUM 这个东西可能有些人不熟,它是在 GIN 基础上扩展出来的索引类型,支持全文检索和附加字段,比普通 B-tree 更适合处理 JSON 里的路径查询。
兼容度到底有多少
这块官方给了比较细的统计,我直接列出来,比看宣传语实在:
| 操作类别 | MongoDB 命令数 | KES 支持数 | 支持率 |
|---|---|---|---|
| 查询和写入命令 | 7 | 7 | 100.00% |
| 查询操作符 | 32 | 31 | 96.88% |
| 更新操作符 | 22 | 22 | 100.00% |
| 投影操作符 | 3 | 3 | 100.00% |
| 聚合管道命令 | 3 | 3 | 100.00% |
| 聚合管道阶段 | 38 | 32 | 84.21% |
| 聚合管道操作符 | 169 | 167 | 98.82% |
| 管理命令 | 32 | 22 | 68.75% |
| 身份认证命令 | 4 | 3 | 75.00% |
| 用户管理命令 | 7 | 5 | 71.43% |
整体兼容度官方说是大于 60%,常用方法 100%。这个数字看着有落差,但其实合理,因为分母里塞了一堆冷门命令。
具体到不支持的,我翻了一下:查询计划缓存相关的 4 个命令是 0 支持,角色管理 10 个命令是 0 支持。不过角色管理这个不算硬伤,KES 自己有一套角色管理机制,用它的管理工具能做。还有 fsync、fsyncUnlock、convertToCapped 这几个也没支持,日常业务基本碰不到。
常用的 CRUD 是全的,insertOne、insertMany、find、findOne、updateOne、updateMany、replaceOne、deleteOne、deleteMany、bulkWrite、aggregate,一个不缺。findOneAndUpdate、findOneAndReplace、findOneAndDelete 这几个也有。
事务这块支持单文档事务和多文档事务。这个挺关键的,MongoDB 早期是不支持多文档事务的,如果你的业务依赖这个特性,迁移的时候要重点测一下。
框架和工具的支持情况
开发框架这块,官方明确列了 Spring Data、Quarkus、Micronaut、Hibernate,还有 pagehelper。
前四个都是主流的选择了,尤其是 Spring Data MongoDB 和 Hibernate,国内项目用得很多。因为它们底层走的也是 MongoDB 驱动,所以驱动层换个连接串,上层框架一般不用动。
工具这块,MongoDB Compass、NoSQL Manager、Navicat、Mongocli 这些都能连。在 Navicat 里选 MongoDB 数据源,填上 KES 的地址端口就行。这一点对 DBA 挺友好的,不用重新学一套工具。
三、基于 SQL 的文档开发,这部分我觉得最有意思
前面说的零代码平替,解决的只是"能不能换"的问题。真正让人觉得有价值的是后面这块:换完之后,你可以用 SQL 去操作文档数据。
这话怎么理解呢?就是说,同一个数据库里,你既能写 db.orders.find({...}) 这种 MongoDB 风格的语句,也能写 SELECT * FROM orders WHERE ... 这种标准 SQL。两边操作的是同一份数据,不需要 ETL,不需要同步。
多模态联查是怎么回事
举个实际点的例子。假设你有一个订单系统,订单主表是关系表,存了订单号、金额、状态、创建时间这些字段。但订单的扩展信息,比如商品快照、优惠明细、物流轨迹,是用文档存的,因为结构不固定。
传统做法是什么?要么在应用层做两次查询再拼装,要么把文档整体塞进一个 text 字段然后自己解析。两种都不太舒服。
在 KES 里,这两种数据可以在一条 SQL 里关联:
这条 SQL 大概长这样(具体语法以实际版本为准,我这里是示意):
SELECT o.order_id, o.amount, d.doc->>'logistics_status' AS logistics
FROM orders o
JOIN order_detail d ON o.order_id = d.id
WHERE o.status = 'PAID'
AND o.create_time > now() - interval '7 days';
->> 这个操作符是从 JSON/BSON 里取文本值。用惯了 PostgreSQL 的人应该很眼熟。
我个人的看法是,这个能力带来的改变不在于"能不能查",而在于开发逻辑简化了。原来一个接口可能要调两次数据库、在代码里做 merge,现在一次查询就出来了。代码量少了,出错的点也少了。
而且它还顺带解决了一个老问题:报表。做报表的同学应该都有体会,文档型数据库做聚合分析是比较吃力的,聚合管道写起来也绕。换成 SQL 之后,窗口函数、CTE 这些都能直接用,写起来顺手很多。
技术栈收敛这件事
还有一个隐性收益,就是技术栈收敛。
原来一套系统里,MySQL 存业务数据,MongoDB 存日志和文档,Redis 做缓存,可能还有个 ES 做检索。四套东西,四套备份策略,四套监控,四个人维护。
现在文档数据这一块并进来了,关系数据本来就在,等于少了一套独立的数据库。运维的人力、备份的窗口、监控的面板,都能省下来一些。
官方在材料里也提到了这个角度:不用为文档数据再引入一套技术栈,直接用 KES 的内置能力做统一存储和管理。这话说得比较克制,但方向是对的。
四、迁移具体要动什么
这部分我按官方给的步骤整理,都是实打实要执行的命令,参数别改。
第一步,初始化实例
initdb 的时候要用 -m 参数指定成兼容 pg 的模式,同时用 -U 指定默认用户为 system:
initdb -m pg -U system
第二步,改 kingbase.conf
这几个参数是必须的:
enable_protocol_compat=on
extension_protocol_port=27017
documentdb_core.bsonUseEJson=on
然后在 shared_preload_libraries 后面追加三项:
shared_preload_libraries='kdb_cron,kdb_documentdb_core,kdb_documentdb'
这里提醒一句,shared_preload_libraries 是要重启才生效的。改完别忘了重启实例,不然插件加载不上,后面创建扩展会报错。
第三步,创建扩展
先连进库里:
ksql -Usystem -p 54321 dbname
然后建扩展和依赖组件:
create extension documentdb cascade;
alter user system with password '123456';
密码 123456 是官方示例里的,生产环境记得换掉,这个不用我多说了。
第四步,验证
用 mongosh 连上去试试:
mongosh "mongodb://system:123456@127.0.0.1:27017/dbname?maxPoolSize=1&directConnection=true&authMechanism=SCRAM-SHA-256"
能连上就说明兼容模式起来了。
整个流程串起来是这样:
数据怎么搬
存量数据这块,最直接的办法是用 mongodump 导出,再用 mongorestore 导进去。因为协议是兼容的,工具能直接用。
不过有个前提要说清楚:BSON 类型在底层是转成关系模型存的,所以导入之后建议做一遍数据校验,特别是嵌套比较深的文档和数组类型,别等到业务跑起来才发现字段丢了。
五、性能这块,得实话实说
官方材料里有一组对比数据,我原样贴出来,因为我觉得这个数据比较真实,没有藏着掖着:
| 数据量 | 项目 | MongoDB | KES with nativeAPI |
|---|---|---|---|
| 1 万条 | INSERT(ms) | 100 | 144 |
| 1 万条 | UPDATE(ms) | 35 | 52 |
| 1 万条 | SELECT 全表(ms) | 24 | 38 |
| 1 万条 | SELECT 单字段(ms) | 10 | 20 |
| 1 万条 | 标量查询(ms) | 4 | 6 |
| 1 万条 | 范围查询(ms) | 4 | 11 |
| 10 万条 | INSERT(ms) | 395 | 473 |
| 10 万条 | UPDATE(ms) | 328 | 543 |
| 10 万条 | SELECT 全表(ms) | 78 | 186 |
| 100 万条 | INSERT(ms) | 2275 | 3498 |
| 100 万条 | UPDATE(ms) | 3083 | 6413 |
| 100 万条 | SELECT 全表(ms) | 2087 | 3320 |
看完什么感觉?单看这张表,KES 在纯文档读写上是慢一些的,数据量越大差距越明显,100 万条那一档的 UPDATE 差了大概一倍。
那为什么会这样呢?原因其实不难理解。MongoDB 是原生的文档存储,BSON 直接落盘,路径最短。KES 这边多了一层协议解析,BSON 还要转成关系模型的存储格式,这个转换本身就有开销。
所以这件事要怎么看?我的判断是这样:
- 如果你的业务是高频写入、海量文档、对单条延迟极其敏感,那 MongoDB 原生的性能优势是客观存在的,迁移之前一定要做压测,别拿这张表当参考就下结论。
- 如果你的业务读写比不高,或者瓶颈大头在应用层和网络层,那这点差距在实际跑起来之后,可能感觉不出来。
- 如果你原来就是关系库加文档库混着用,那省掉一次跨库调用、省掉一套 ETL,整体链路反而可能更快。
最后那句是我自己的经验,不一定适用于所有人,仅供参考。
六、几个容易踩的坑
写到最后,把我想到的几个点列一下。
第一,别拿兼容度当 100%。 常用命令确实齐了,但聚合管道阶段有 84.21%,管理命令只有 68.75%。上线前一定要把自己的语句全跑一遍,尤其是用了比较少见聚合阶段的场景。
第二,注意多文档事务的行为差异。 MongoDB 的事务有它自己的一套语义,跨过去之后行为细节可能不完全一样,涉及金额、库存这类场景要重点测。
第三,shared_preload_libraries 改完必须重启。 这个前面说过,但确实是新手最容易忘的一步。
第四,安全这块其实是加分项。 KES 从访问控制、身份鉴别到传输安全、存储安全、事后审计,是一整套的。相比 MongoDB 的防护措施要完整一些,这也是不少项目迁移的主要动因之一。
七、小结
回到最开始那个问题。MongoDB 迁移这件事,路子不止一条。
如果你的核心诉求是"合规 + 收敛技术栈 + 少维护一套东西",那 KES 这个 MongoDB 兼容版是值得看一眼的。零代码平替这块确实做出来了,改个连接串就能跑,开发框架也不用大改。基于 SQL 的文档开发是个加分项,尤其是需要文档和关系数据联查的场景,能省不少代码。
但如果你的场景是极致性能导向的,那就要谨慎一点,先压测再决定。
工具和产品都是手段,能解决问题就行。有什么想法评论区聊,我看到都会回。
更多推荐


所有评论(0)