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

在这里插入图片描述

上个月帮朋友评估一个数据库选型的事。他们有个 MongoDB 集群要换,一问原因,倒不是嫌 MongoDB 不好用,主要是合规那边过不去,加上运维两套技术栈实在有点费人。聊到 MongoDB 迁移这个话题,我发现很多人第一反应是"文档库换文档库",其实还有另一条路:换成一个能用 SQL 处理文档的关系库。

这篇就聊聊金仓的 KES MongoDB 兼容版,重点讲三件事:它怎么做到零代码平替、基于 SQL 的文档开发到底怎么用、还有迁移的时候具体要动哪些配置。

一、这东西本质上是个双协议数据库

先把定位说清楚,不然后面容易绕。

KES MongoDB 兼容版不是另起炉灶做的一个新产品,它是 KES 上的一个能力扩展。同一个数据库实例,同时开两个端口:27017 给 MongoDB 协议用,54321 给 KES 自己的协议用。

也就是说,你的老应用继续用 MongoDB 驱动连 27017,新应用用 KES 驱动连 54321,两边看的是同一份数据。

架构大概长这样:

存储层

语法层

协议层

网络层

应用层

基于 MongoDB 驱动的应用

基于 KES 驱动的应用

端口 27017

端口 54321

MongoDB 协议组件

KES 协议

协议调度

MongoDB-parser

Oracle-parser

PGSQL-parser

文档数据

关系数据

向量数据

GIS 数据

这里有个细节值得注意。语法层是分开的,MongoDB 的请求走 MongoDB-parser,SQL 请求走 Oracle-parser 或者 PGSQL-parser,但到了存储层是共用的。这就是它能把关系数据和文档数据放在一起查的前提。

二、"零代码平替"是怎么实现的

官方给的说法是"仅需要修改驱动层的连接指向,应用零修改"。

听起来挺夸张,但拆开看逻辑是通的。因为它在协议层面做兼容,不是去做 API 转换。你的应用发出去的还是 MongoDB 的原生协议报文,KES 这边有个协议组件接住,解析完之后转成内部的执行计划。对应用来说,它并不知道对端已经换人了。

对象模型这一层也做了对应关系:

KES 内部实现

MongoDB 概念

database

collection

document

index

database

table

row

索引

也就是说,一个 collection 在底层就是一张表,一个 document 就是一行。BSON 类型这块,是靠 KES 关系模型的自定义类型、自定义函数、自定义访问方法这几个能力拼出来的。

索引也做了对应。_id 字段会自动建 B-TREE 索引,你手动执行 db.products.createIndex({"item":1}) 的时候,底层建的是一个 RUM 索引。RUM 这个东西可能有些人不熟,它是在 GIN 基础上扩展出来的索引类型,支持全文检索和附加字段,比普通 B-tree 更适合处理 JSON 里的路径查询。

兼容度到底有多少

这块官方给了比较细的统计,我直接列出来,比看宣传语实在:

操作类别MongoDB 命令数KES 支持数支持率
查询和写入命令77100.00%
查询操作符323196.88%
更新操作符2222100.00%
投影操作符33100.00%
聚合管道命令33100.00%
聚合管道阶段383284.21%
聚合管道操作符16916798.82%
管理命令322268.75%
身份认证命令4375.00%
用户管理命令7571.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 JOIN
按 _id 关联

SQL JOIN
按 _id 关联

orders 表
order_id / amount / status / create_time

order_detail 集合
商品快照 / 优惠明细 / 物流轨迹

统一查询结果

报表 / 接口 / 分析

这条 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"

能连上就说明兼容模式起来了。

整个流程串起来是这样:

否

是

准备阶段

initdb -m pg -U system
初始化兼容模式实例

修改 kingbase.conf
开启协议兼容 指定 27017 端口

shared_preload_libraries
追加 3 个 kdb 组件

重启数据库实例
让预加载生效

ksql 登录 执行
create extension documentdb cascade

mongosh 连接 27017
验证兼容模式

连接成功?

检查端口占用
检查扩展是否创建成功

导入存量数据
跑业务回归测试

切换应用连接串
灰度上线

数据怎么搬

存量数据这块,最直接的办法是用 mongodump 导出,再用 mongorestore 导进去。因为协议是兼容的,工具能直接用。

不过有个前提要说清楚:BSON 类型在底层是转成关系模型存的,所以导入之后建议做一遍数据校验,特别是嵌套比较深的文档和数组类型,别等到业务跑起来才发现字段丢了。

五、性能这块,得实话实说

官方材料里有一组对比数据,我原样贴出来,因为我觉得这个数据比较真实,没有藏着掖着:

数据量项目MongoDBKES with nativeAPI
1 万条INSERT(ms)100144
1 万条UPDATE(ms)3552
1 万条SELECT 全表(ms)2438
1 万条SELECT 单字段(ms)1020
1 万条标量查询(ms)46
1 万条范围查询(ms)411
10 万条INSERT(ms)395473
10 万条UPDATE(ms)328543
10 万条SELECT 全表(ms)78186
100 万条INSERT(ms)22753498
100 万条UPDATE(ms)30836413
100 万条SELECT 全表(ms)20873320

看完什么感觉?单看这张表,KES 在纯文档读写上是慢一些的,数据量越大差距越明显,100 万条那一档的 UPDATE 差了大概一倍。

那为什么会这样呢?原因其实不难理解。MongoDB 是原生的文档存储,BSON 直接落盘,路径最短。KES 这边多了一层协议解析,BSON 还要转成关系模型的存储格式,这个转换本身就有开销。

所以这件事要怎么看?我的判断是这样:

  • 如果你的业务是高频写入、海量文档、对单条延迟极其敏感,那 MongoDB 原生的性能优势是客观存在的,迁移之前一定要做压测,别拿这张表当参考就下结论。
  • 如果你的业务读写比不高,或者瓶颈大头在应用层和网络层,那这点差距在实际跑起来之后,可能感觉不出来。
  • 如果你原来就是关系库加文档库混着用,那省掉一次跨库调用、省掉一套 ETL,整体链路反而可能更快。

最后那句是我自己的经验,不一定适用于所有人,仅供参考。

六、几个容易踩的坑

写到最后,把我想到的几个点列一下。

第一,别拿兼容度当 100%。 常用命令确实齐了,但聚合管道阶段有 84.21%,管理命令只有 68.75%。上线前一定要把自己的语句全跑一遍,尤其是用了比较少见聚合阶段的场景。

第二,注意多文档事务的行为差异。 MongoDB 的事务有它自己的一套语义,跨过去之后行为细节可能不完全一样,涉及金额、库存这类场景要重点测。

第三,shared_preload_libraries 改完必须重启。 这个前面说过,但确实是新手最容易忘的一步。

第四,安全这块其实是加分项。 KES 从访问控制、身份鉴别到传输安全、存储安全、事后审计,是一整套的。相比 MongoDB 的防护措施要完整一些,这也是不少项目迁移的主要动因之一。

七、小结

回到最开始那个问题。MongoDB 迁移这件事,路子不止一条。

如果你的核心诉求是"合规 + 收敛技术栈 + 少维护一套东西",那 KES 这个 MongoDB 兼容版是值得看一眼的。零代码平替这块确实做出来了,改个连接串就能跑,开发框架也不用大改。基于 SQL 的文档开发是个加分项,尤其是需要文档和关系数据联查的场景,能省不少代码。

但如果你的场景是极致性能导向的,那就要谨慎一点,先压测再决定。

工具和产品都是手段,能解决问题就行。有什么想法评论区聊,我看到都会回。

Logo

一站式 AI 云服务平台

更多推荐