它以为自己连的是MongoDB

前两天有幸拿到 KingbaseES V9R2C13(MongoDB兼容版)内测软件包,我第一件事是找台机器装上,然后把 Node.js 的 mongodb 驱动直接连过去。验证是否真的"兼容 MongoDB"。

环境是一台 Oracle Linux 8.10 x86 主机。软件放 /opt/kingbase/KingbaseES,数据放 /opt/kingbase/data,kingbase 用户跑库,systemd 管进程。

服务起来之后有个细节值得说:同一个 KingbaseES 进程监听了两个端口。54321 走 SQL,27017 走 MongoDB 协议。一套实例,两个入口。

驱动用的是 mongodb@6.21.0。基础回归过了 19 个场景,深度探测过了 38 个。几组我心里没底的用例,另外在 MongoDB 7.0.28 上跑了同一份 Node.js 脚本,两边对返回值和数据变化。

关于安装

原厂 ISO,setup.sh -i console,一路装完。目录和权限按常规数据库服务的习惯来:软件目录 /opt/kingbase/KingbaseES,数据目录 /opt/kingbase/data,kingbase 用户运行数据库,systemd 负责服务启停和开机自启。

MongoDB 协议这块能力来自 DocumentDB 扩展。测试库装了三个,版本都是 0.110-0:

text 复制代码
documentdb_core
documentdb
documentdb_extended_rum

代码还是熟悉的写法

驱动用 MongoDB URI 连接,认证方式按手册显式指定成 SCRAM-SHA-256。应用代码那边继续用 MongoClient、Db、Collection,没有额外包装。

javascript 复制代码
const { MongoClient } = require('mongodb');

const uri =
  'mongodb://<user>:<password>@127.0.0.1:27017/test' +
  '?authSource=admin' +
  '&authMechanism=SCRAM-SHA-256' +
  '&directConnection=true';

const client = new MongoClient(uri);
await client.connect();

const db = client.db('test');
const users = db.collection('users');
await users.insertOne({ name: 'Alice', tags: ['mongo', 'kingbase'] });

console.log(await users.findOne({ name: 'Alice' }));
await client.close();

握手回来 hello.ok = 1,maxWireVersion = 21,buildInfo.version = 7.0.42。驱动那边认为自己连的是一个 7.0 的 MongoDB。后面所有集合创建、文档写入、查询和更新,都在这个连接上做完。

BSON 和文档操作

先测的是 ObjectId、Date、Binary、Decimal128、数组和嵌套文档的写入与读取。深度用例还专门塞了 4MB 的文档和 50 层嵌套的文档,读回来核对长度和层级。

查询覆盖了范围条件、逻辑条件、数组匹配、exists、exists、exists、type、regex 和 expr。排序、分页、投影、计数、distinct 也都进了回归脚本。

更新这块用例最多:inc、inc、inc、mul、min、min、min、max、currentDate、currentDate、currentDate、rename、unset、unset、unset、bit,加上 push、push、push、pull、addToSet、addToSet、addToSet、pop 这些数组操作。

arrayFilters 我是单独挑出来测的。订单明细、库存条目、多层配置,改数组里指定的某一条是日常写法,兼容层最容易在这种地方露馅。

javascript 复制代码
await orders.updateOne(
  { orderNo: 'SO-20260723' },
  { $set: { 'items.$[item].status': '已发货' } },
  { arrayFilters: [{ 'item.sku': 'SKU-1001' }] }
);

结果是通过的:只有 SKU-1001 那条明细的状态变了,同一数组里的其他明细原样没动。upsert、$setOnInsert、replaceOne、updateMany、deleteMany 一并过了。bulkWrite 的有序和无序两种模式分别跑,写入条数和最终文档内容都和预期一致。

TTL 确实删掉了过期文档

索引测了普通、复合、唯一、稀疏、部分、TTL、文本、通配符和哈希。唯一索引撞到重复值时,驱动收到 11000,和原生一致。

TTL 这一项我没停在"索引建出来了"。元数据建得出来,不代表后台任务真的在跑。所以写了一条已经过期的文档,等后台任务执行,再去查剩余数量,结果是 0。

javascript 复制代码
await sessions.createIndex(
  { expireAt: 1 },
  { expireAfterSeconds: 0 }
);

collMod 改 TTL 过期时间也测了,命令会返回旧值和新值。英文文本索引用 text 加 search 查关键词,没问题。

聚合跑到了关联、递归和窗口计算

基础管道这一层没什么悬念:match、match、match、project、group、group、group、sort、facet、facet、facet、bucket、bucketAuto、bucketAuto、bucketAuto、sortByCount、$sample。

真正想看的是重的那几个。跨集合的 lookup、合并结果集的 unionWith、递归查询的 graphLookup,各写了单独用例。graphLookup,各写了单独用例。graphLookup,各写了单独用例。setWindowFields 的排名和窗口计算也过了。

javascript 复制代码
const result = await orders.aggregate([
  { $match: { status: '已完成' } },
  { $group: {
      _id: '$customerId',
      totalAmount: { $sum: '$amount' },
      orderCount: { $sum: 1 }
  } },
  { $sort: { totalAmount: -1 } },
  { $limit: 20 }
]).toArray();

out把聚合结果写进集合,一次通过。out 把聚合结果写进集合,一次通过。out把聚合结果写进集合,一次通过。merge 一开始报错,按 MongoDB 对 on 字段唯一索引的要求补上索引之后完成了合并。这个行为和原生也是一样的。

JSON Schema 拦得住不合规文档

测试环境开了 documentdb.enableSchemaValidation=on。建集合时定义 $jsonSchema,要求 name 必填且是字符串,age 是非负整数。

javascript 复制代码
await db.createCollection('validated_users', {
  validator: {
    $jsonSchema: {
      bsonType: 'object',
      required: ['name'],
      properties: {
        name: { bsonType: 'string' },
        age: { bsonType: 'int', minimum: 0 }
      }
    }
  }
});

合规文档正常写入。缺 name、name 类型不对、age 小于 0,数据库都返回 121 Document failed validation。也就是说,一部分基础数据规则可以压到集合上,不用全堆在应用层。

事务和 GridFS

事务从 startSession 开始,提交和回滚分别测。提交后查得到,回滚后查不到,多集合事务用同样的方式核对。

还有一条用例我比较在意:起一个活动事务,再用 killSessions 把会话干掉。会话结束之后,那个事务的未提交写入没有留在集合里。这条要是不通过,应用异常断连的场景会很难收拾。

GridFS 就是走完一圈:上传文件、下载、比对内容、删除。

javascript 复制代码
const bucket = new GridFSBucket(db, { bucketName: 'attachments' });
const uploadId = new ObjectId();

await new Promise((resolve, reject) => {
  const stream = bucket.openUploadStreamWithId(uploadId, 'report.pdf');
  stream.once('finish', resolve);
  stream.once('error', reject);
  stream.end(Buffer.from('report content'));
});

这轮测下来,我的判断

兼容度比我预期的高。

19 加 38 个场景,从 BSON 类型、CRUD、索引,一路到聚合、JSON Schema、事务和 GridFS,官方驱动全程没换写法,也没有为了绕开哪个不支持的特性去改代码。graphLookup、graphLookup、graphLookup、setWindowFields、arrayFilters、TTL 后台回收、killSessions 之后的事务清理,这几条是我事先觉得最容易出问题的,结果都过了。$merge 那次报错也不算兼容问题,补上 on 字段的唯一索引就通过,要求和原生 MongoDB 一致。

对已有项目来说,改造入口很清楚:换掉测试环境的连接串,把集合初始化、核心读写、索引和聚合管道跑一遍回归,跑完就知道数据层要动多少。以这次的结果看,用 Node.js 驱动的应用大概率不用动多少代码。

双端口是我觉得最实用的一点。老系统继续走 54321 的 SQL 口,新应用走 27017 的 MongoDB 口,数据平台按自己的习惯选。存量系统改造最怕的就是"一次性切换",这个设计直接把它变成了可以分阶段推进的事。

这轮的定位是功能验证,结论是能用,而且用得很顺。接下来我打算拿真实的集合结构和数据量再跑一轮,重点看性能、复制和高可用。功能这一层已经看清楚了,剩下的是把量压上去。各位朋友觉得Kingbase的这个版本如何?

相关推荐
DBA小马哥5 分钟前
向量数据库入门到进阶:Embedding、ANN算法与RAG落地的关键术语
数据库·算法·embedding
云和数据.ChenGuang8 分钟前
fastapi项目拆分实战数据模型
java·服务器·数据库·人工智能·深度学习·fastapi·强化学习
祈禾13 分钟前
Redis三大特殊数据类型
运维·服务器·数据库·redis·笔记·缓存
东方护航数据恢复(深圳)24 分钟前
MySQL_Oracle数据库崩溃修复全攻略_东方护航数据恢复深圳店
数据库·mysql·oracle
y = xⁿ1 小时前
一文掌握Redis常见八股
数据库·redis·缓存
笔墨登场说说1 小时前
mongodb备份
mongodb
Cloud云卷云舒1 小时前
HaishanDB(海山)|磐维数据库|YashanDB(崖山)深度对比分析
数据库·人工智能·海山数据库·haishandb·移动云海山数据库
weixin_460443561 小时前
企业考试系统如何对接OA、钉钉和企业微信?SSO单点登录、组织同步与权限一致性设计
java·开发语言·数据库
xywww1681 小时前
真实后台页实测:Opus 5 看图写前端的可用边界在哪
linux·服务器·前端·数据库·人工智能·gpt
上海云盾商务经理杨杨2 小时前
SQL 盲注入渗透实战!无报错页面也能成功注入
数据库·sql