我们组维护的一套会员运营系统跑在 MySQL 8.0 上,这套系统有个很典型的"历史包袱":当年为了快速迭代,会员的标签、偏好设置这类结构不稳定的数据,全都塞进了 JSON 字段。几年下来,业务代码里 ->、->>、JSON_OVERLAPS 满天飞,报表 SQL 里还夹杂着 FORMAT、TIME_FORMAT、INSERT() 这类 MySQL 味道很重的函数。
最近在做数据库国产化替代的选型评估,KingbaseES 的 MySQL 兼容版进入了候选名单。宣传材料上"业务无缝迁移"说得很好,但对我们这种 JSON 重度用户来说,真正的问题只有一个:这些 JSON 操作符和函数,不改一行业务 SQL,能不能直接跑起来?
与其看文档列表,不如自己动手。正好赶上金仓社区的"产品体验官"活动,我把业务里最常用的 JSON 场景抽象成一组测试用例,在 KingbaseES V9R3C18(MySQL 兼容版)上逐条验证。先说结论:大面上确实能"平替",但我也实打实测出了两个函数级差异和一个显示差异——这些细节反而是本文最有价值的部分。
打开金仓官网下载页选好 V9R3C18(MySQL 兼容版)后遇到第一个意外:安装包只有 Linux 版——X64 通用版,加上海光、飞腾、鲲鹏、兆芯四个国产芯片专用包,没有 Windows 版。我本来打算在本机 Windows 上装,只好换方案,翻出一台闲置的鲲鹏架构云服务器,正好用上官方的鲲鹏(Aarch64)专用包,顺便验证一下"国产数据库 + 国产芯片"这套组合的成色。
最终测试环境:
服务器:鲲鹏(aarch64)云主机,8G 内存
操作系统:Ubuntu 22.04.5 LTS
数据库:KingbaseES V9R3C18(MySQL 兼容版),KingbaseES_V009R003C018B0003_Kunpeng64_install.iso(1.2G)
授权:官网提供的开发版授权文件(免费,有效期 365 天)
对照组:MySQL 8.0(业务现网版本)

两个实用提示:
提示一:服务器上直接 wget 下载会被 OSS 防盗链拦截,报 AccessDenied: You are denied by bucket referer policy,加个 Referer 头就行:
复制代码wget --referer="https://www.kingbase.com.cn/" "<安装包下载地址>"
下载完记得校验,我这边 md5sum 和官网给的 0044c38465e337d601fed1d1f677aaa9 完全一致。
提示二:安装器强制非 root 用户运行(root 执行 setup.sh 会直接退出提示换用户),并且支持静默安装,全程不需要图形界面,对云服务器非常友好:
复制代码# root 下准备用户和目录
useradd -m -s /bin/bash kingbase
mkdir -p /opt/Kingbase && chown -R kingbase:kingbase /opt/Kingbase
mount -o loop KingbaseES_V009R003C018B0003_Kunpeng64_install.iso /mnt/kes# 静默安装配置(ISO 里有模板 setup/silent.cfg,改两行即可)
cat > /opt/silent_my.cfg <<EOF
INSTALL_PATH=/opt/Kingbase/ES/V9
LICENSE_PATH=/opt/license_dev365.dat
COMPONENTS=Server,Interface
CREATE_SHORTCUT=no
EOF# 切到 kingbase 用户执行静默安装
su - kingbase -c "sh /mnt/kes/setup.sh -i silent -f /opt/silent_my.cfg"
一分钟左右打出 Installation Success 100%。
接着踩到本次唯一一个真正的坑:初始化时报 libsasl2.so.3: cannot open shared object file。原因是金仓的二进制按 RHEL/国产 OS 系打包(cyrus-sasl 的 soname 是 .so.3),而 Ubuntu 同一个库的 soname 是 .so.2。库本身是同一个东西,做个软链即可:
复制代码ln -s /usr/lib/aarch64-linux-gnu/libsasl2.so.2 /usr/lib/aarch64-linux-gnu/libsasl2.so.3
ldconfig
这也提醒我们:官方兼容性清单以国产操作系统和 RHEL 系为主,Ubuntu 用户要有处理这类小差异的心理准备。
然后初始化实例。V9R3C18 的 initdb 提供了 -m mysql 参数(这个版本只支持 mysql 模式,可见就是为 MySQL 迁移场景定制的),我还加了 --enable-ci 让字符串比较大小写不敏感,进一步贴近 MySQL 的默认行为:
复制代码initdb -D /opt/Kingbase/ES/V9/data -m mysql -E UTF8 --enable-ci -U system --pwfile=<密码文件>
sys_ctl -D /opt/Kingbase/ES/V9/data -l kingbase.log start
注意默认端口是 54321,不是 MySQL 的 3306(从 MySQL 过来的人第一次连基本都会在这愣一下)。ksql 连上确认版本:
复制代码SELECT version();
-- version
-- -------------------------
-- KingbaseES V009R003C018

为了让测试贴近业务而不是"Hello World 式"地过函数列表,我把线上表结构简化成一张 member 表:tags 存会员标签(JSON 数组),profile 存会员画像(嵌套 JSON 对象)——这是我们线上最典型的两种 JSON 用法。
复制代码CREATE TABLE member (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
phone VARCHAR(20),
points INT DEFAULT 0,
tags JSON,
profile JSON,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);INSERT INTO member (name, phone, points, tags, profile) VALUES
('张伟', '13812345678', 12580, '["vip","sports","digital"]',
'{"level":3,"city":"北京","prefs":{"push":true,"email":false}}'),
('李娜', '13987654321', 3200, '["reading","travel"]',
'{"level":2,"city":"上海","prefs":{"push":false,"email":true}}'),
('王强', '13711112222', 860, '["sports","travel","food"]',
'{"level":1,"city":"北京","prefs":{"push":true,"email":true}}'),
('赵敏', '13633334444', 25999, '["vip","luxury"]',
'{"level":4,"city":"深圳","prefs":{"push":true,"email":false}}');
这一步先给了我一个好印象:AUTO_INCREMENT、DATETIME DEFAULT CURRENT_TIMESTAMP、JSON 字面量插入,全部原样通过,一个语法错误都没有。
-> 与 ->> 操作符——引号、优先级、布尔值MySQL 里这对操作符的核心区别:-> 返回 JSON 类型(字符串带双引号),->> 返回去引号后的文本。业务代码里两种写法混用得很厉害,必须都验证:
复制代码SELECT name,
profile->'$.city' AS city_json,
profile->>'$.city' AS city_text
FROM member;
实测输出:
复制代码name | city_json | city_text
------+-----------+-----------
张伟 | "北京" | 北京
李娜 | "上海" | 上海
王强 | "北京" | 北京
赵敏 | "深圳" | 深圳
city_json 带引号、city_text 裸文本,和 MySQL 分毫不差。接着是 WHERE 条件和运算优先级,这才是真正决定"业务 SQL 要不要改"的地方:
复制代码-- 1. ->> 取文本后和字符串比较:命中 张伟、王强
SELECT name FROM member WHERE profile->>'$.city' = '北京';-- 2. -> 取 JSON 标量后和数字比较:命中 张伟、赵敏
SELECT name FROM member WHERE profile->'$.level' >= 3;-- 3. 优先级验证:正确解析为 (profile->>'$.level') + 1,输出 4/3/2/5
SELECT name, profile->>'$.level' + 1 AS next_level FROM member;-- 4. 嵌套路径 + 布尔值,两种写法:均命中 张伟、王强、赵敏
SELECT name FROM member WHERE profile->>'$.prefs.push' = 'true';
SELECT name FROM member WHERE profile->'$.prefs.push' = true;
第 3 条是活动海报里点名的"操作符优先级"问题:如果解析器优先级处理不对,->>'$.level' + 1 可能被解析成对路径做加法直接报错。实测正确地先取值再运算。第 4 条的 JSON 布尔值和 SQL 布尔值隐式转换是各家数据库最容易出差异的角落,两种写法结果都与 MySQL 一致——这四条全绿,说明 ->/->> 这一层可以放心。

顺手测了一个 MySQL 做不到的写法——->**** 链式调用:
复制代码SELECT profile->'$.prefs'->>'$.push' AS probe_chain FROM member LIMIT 1;
-- 输出:true
MySQL 8.0 里 -> 左侧必须直接是 JSON 列,这种链式写法会报语法错误;KingbaseES 直接支持。这是它 PostgreSQL 基因带来的超集能力:迁移过去的旧 SQL 不用改,新写的 SQL 还能更灵活。
JSON_OVERLAPS 是 MySQL 8.0.17 才加入的较新函数,判断两个 JSON 文档是否有交集。我们运营后台用它做"任一标签命中"的人群圈选,属于一旦不兼容就要改代码的硬依赖:
复制代码-- 圈出打了 sports 或 travel 任一标签的会员("或"语义)
SELECT name, tags FROM member
WHERE JSON_OVERLAPS(tags, '["sports","travel"]');-- 对比:JSON_CONTAINS 要求候选集整体被包含("且"语义)
SELECT name, tags FROM member
WHERE JSON_CONTAINS(tags, '["sports","travel"]');
实测输出:
复制代码-- JSON_OVERLAPS:三人命中
name | tags
------+------------------------------
张伟 | ["vip", "sports", "digital"]
李娜 | ["reading", "travel"]
王强 | ["sports", "travel", "food"]-- JSON_CONTAINS:仅王强(两个标签都有)
name | tags
------+------------------------------
王强 | ["sports", "travel", "food"]
一个"或"一个"且",两个函数的结果集都与 MySQL 精确一致。连 JSON_OVERLAPS 这种 8.0.17 才出现的新函数都覆盖到了,兼容的版本追得比我预期的紧。

前面一路全绿,到报表函数这里终于出现了本次测试最有价值的发现。还是用真实风格的"脱敏报表"来测:
复制代码SELECT
CONCAT(LEFT(name, 1), '**') AS masked_name,
INSERT(phone, 4, 4, '****') AS masked_phone,
FORMAT(points, 0) AS points_display
FROM member
WHERE profile->>'$.city' = '北京';
差异一:INSERT() 字符串函数不存在。 上面的查询直接报错:
复制代码ERROR: function INSERT(varchar, integer, integer, unknown) does not exist
LINE 3: INSERT(phone, 4, 4, '****') AS masked_phone,
HINT: No function matches the given name and argument types.
MySQL 里 INSERT(str, pos, len, newstr) 是个和 INSERT INTO 语句同名的字符串替换函数,我们用它做手机号打码。KingbaseES V9R3C18 没有实现这个函数。好在替代方案是现成的,实测两种都可行:
复制代码-- 方案 A:PostgreSQL 系的 OVERLAY,语义完全对应
SELECT OVERLAY(phone PLACING '****' FROM 4 FOR 4) FROM member;
-- 输出:138****5678 -- 方案 B:LEFT + RIGHT 拼接,两边数据库通用
SELECT CONCAT(LEFT(phone,3), '****', RIGHT(phone,4)) FROM member;
-- 输出:138****5678
差异二:FORMAT() 不加千分位。 把 INSERT() 从查询里拿掉后报表能跑了,但 FORMAT(points, 0) 输出的是 12580——MySQL 会输出 12,580(千分位逗号正是这个函数在报表里存在的意义)。函数存在、不报错、但行为不同,这种"静默差异"比直接报错更值得警惕,因为它能骗过冒烟测试,直到业务方盯着报表问"逗号哪去了"。
差异三(显示层):|| 的语义与显示。 MySQL 默认把 || 当逻辑或(除非开 PIPES_AS_CONCAT),PG 系把它当字符串拼接,这是两个阵营的经典分歧。实测 KingbaseES 的 MySQL 模式语义站在了 MySQL 这边:
复制代码SELECT profile->>'$.city' || '市' FROM member LIMIT 1; -- 输出 f(当作逻辑或运算)
SELECT 1 || 0 AS or_probe; -- 输出 t
语义对齐了,但布尔结果显示为 PG 风格的 t/f 而不是 MySQL 的 1/0——SQL 层不用改,但如果应用代码里有按字符串 '1'/'0' 解析布尔结果的逻辑就要留意。迁移建议很简单:字符串拼接一律写 ****CONCAT(),它在两边行为完全一致。
剩下两个函数顺利通过:
复制代码SELECT TIME_FORMAT('09:05:30', '%H小时%i分'); -- 09小时05分
SELECT TIME_FORMAT('134:05:30', '%H小时%i分'); -- 134小时05分 (>24h 的 TIME 也正确)-- QUOTE 生成补数 SQL,转义正确
SELECT CONCAT('UPDATE member SET points = points + 100 WHERE name = ',
QUOTE(name), ';')
FROM member WHERE JSON_OVERLAPS(tags, '["vip"]');
-- 输出:UPDATE member SET points = points + 100 WHERE name = '张伟'; 等两条

以下均为本人在鲲鹏服务器上的真实测试结果(KingbaseES V009R003C018):
| 测试项 | 业务场景 | MySQL 8.0 | KingbaseES V9R3C18 |
|---|---|---|---|
-> / ->> 基础提取 | 画像字段读取 | 完全一致 | |
->> 参与算术运算(优先级) | 等级计算 | 先取值后运算 | |
| 嵌套路径 + JSON 布尔比较 | 推送偏好过滤 | 两种写法均一致 | |
JSON_OVERLAPS | 标签圈人(或语义) | 8.0.17+ | 结果集一致 |
JSON_CONTAINS | 标签圈人(且语义) | 结果集一致 | |
LEFT / RIGHT / CONCAT | 姓名/手机脱敏 | 一致 | |
QUOTE | 动态 SQL 转义 | 一致 | |
TIME_FORMAT(含 >24h) | 时长格式化 | 一致 | |
INSERT() 字符串函数 | 手机号打码 | 函数不存在,用 OVERLAY 或 LEFT+RIGHT 替代 | |
FORMAT() 千分位 | 报表数字展示 | 12,580 | ️ 输出 12580,无千分位 |
-> 链式调用 | 深层 JSON 提取 | 不支持 | 支持(超集能力) |
12 个测试点:9 个完全一致,1 个函数缺失(有平滑替代),1 个行为差异,1 个显示差异,外加 1 个超集能力。
结论:以我们这套 JSON 重度使用的会员系统为样本,KingbaseES V9R3C18 的 MySQL 兼容做到了"核心路径零改造"——JSON 操作符、JSON 函数、主流字符串函数原样可用;但它不是 100% 的像素级复刻,INSERT() 缺失和 FORMAT() 千分位差异说明迁移前的兼容性扫描不能省。 对选型来说这是个合格偏好的答卷:差异都集中在函数边缘地带,且都有一行代码级的替代方案;而链式 -> 这类超集能力算是意外之喜。
安装体验上,静默安装对云服务器很友好,开发版授权免费好拿;扣分项是 Ubuntu 不在一线支持清单(libsasl2 软链那个坑)和 Windows 安装包的缺席。
给金仓团队的几点建议(也算体验官的本职):
补齐 INSERT() 字符串函数,并让 FORMAT() 输出千分位——这两个都是报表场景高频函数,属于"补上就少一批迁移工单"的性价比之王。
提供"MySQL 函数/操作符兼容矩阵"官方速查表,把"完全一致 / 行为有差异 / 未实现+替代方案"标清楚。我这篇文章本质上就是在人肉生成这张表的一小角。
Ubuntu/Debian 系的依赖提示:安装器检测到缺 libsasl2.so.3 时给出软链或安装建议,比让用户自己 ldd 排查友好得多。
布尔值显示:MySQL 模式下把布尔结果显示为 1/0 而非 t/f,能再消灭一类应用层适配。
下一篇我计划测 MySQL 原生驱动/JDBC 直连 + 窗口函数与 PREPARE/EXECUTE,把"从兼容到进阶"这条路走完。有同样在做替代评估的朋友,欢迎评论区交流你们踩过的坑。
会写代码的 AI 很多:能算 Coding Agent 的没几个
耗时 7 天:我们开源了腾讯 WorkBuddy 实战蓝皮书 建议收藏
从 PRD 到可交付代码的结构化工法:Harness vibecoding
LangChain v1.3.4 笔记 - 01 模型的连接/输出 消息 提示词模板
Python 与 PyTorch 工程实践:从脚本到训练框架
Claude 关键技能 Code Review