【金仓数据库征文】MySQL至金仓KES异构数据库平滑迁移与性能深度调优实战

信创产业推进的大背景下,国产数据库替换已经从可选变成了很多团队的必选项。身边不少后端开发和运维的朋友聊起金仓这类国产数据库,普遍有三个刻板印象:语法适配麻烦、性能跟不上MySQL、出了问题资料少难排查,真要动手落地又不知道从哪一步开始。


目录

一、实验环境与前置准备

二、金仓KES本地部署与基础环境验证

[2.1 安装包获取与图形化安装实操](#2.1 安装包获取与图形化安装实操)

[2.2 数据库实例可视化初始化](#2.2 数据库实例可视化初始化)

[2.3 服务状态核验与DBeaver连接环境验证](#2.3 服务状态核验与DBeaver连接环境验证)

四、基于KDT工具的迁移兼容性检测

[4.1 KDT工具新建评估任务](#4.1 KDT工具新建评估任务)

[4.2 扫描结果分类排查](#4.2 扫描结果分类排查)

[五、数据库结构适配 + 自定义兼容改造](#五、数据库结构适配 + 自定义兼容改造)

[5.1 字段类型手动适配改造](#5.1 字段类型手动适配改造)

[5.2 自定义兼容函数实现](#5.2 自定义兼容函数实现)

[六、全量数据迁移 + 数据一致性校验](#六、全量数据迁移 + 数据一致性校验)

[6.1 结构与数据分步迁移](#6.1 结构与数据分步迁移)

[6.2 迁移耗时实测](#6.2 迁移耗时实测)

[6.3 数据一致性校验](#6.3 数据一致性校验)

[6.4 轻量增量同步模拟](#6.4 轻量增量同步模拟)

七、业务SQL深度优化实战

[7.1 大偏移量深度分页优化](#7.1 大偏移量深度分页优化)

[7.2 JSON字段检索性能优化](#7.2 JSON字段检索性能优化)

八、金仓数据库个性化参数调优

九、KWR性能报告实操,自主定位数据库瓶颈

[9.1 KWR插件开启](#9.1 KWR插件开启)

[9.2 生成性能报告实操](#9.2 生成性能报告实操)

[9.3 瓶颈分析与优化](#9.3 瓶颈分析与优化)

十、极简主备同步高可用测试

[10.1 主库配置](#10.1 主库配置)

[10.2 备库克隆与启动](#10.2 备库克隆与启动)

[10.3 同步验证](#10.3 同步验证)

十一、迁移全流程踩坑总结

十二、全文总结与国产化实践感悟


这次我索性在自己的本地办公电脑上,从零开始跑通完整的迁移调优全链路:从金仓KES V9安装部署、兼容性检测、结构适配改造、全量+增量数据迁移,到业务SQL优化、数据库参数调优、性能报告分析、轻量主备搭建,全程没有企业服务器环境,所有步骤、报错、性能数据都是自己亲手实测、反复调试出来的。

整理这篇复盘,没有空话套话,所有命令、SQL、配置都可以直接复现。一方面是记录自己的国产化学习实操过程,另一方面也给中小型业务的数据库国产化迁移提供一份可落地的参考,帮大家少踩我踩过的坑。


一、实验环境与前置准备

1.1 本地硬件与软件版本

我的本地测试机配置:Intel i5-12400(6核12线程)、16G DDR4 3200内存、512G NVMe固态,系统为Windows 10 专业版22H2。

软件版本严格对齐常用生产环境:

  • 源数据库:MySQL 8.0.36 社区版,默认端口3306

  • 目标数据库:人大金仓 KingbaseES V9.0.3 个人版,默认端口54321

  • 迁移工具:金仓官方 KDT 迁移工具 V2.3

  • 数据库客户端:DBeaver 24.0.1

  • 性能验证:纯SQL执行耗时统计 + 自定义Python脚本压测

1.2 测试库与测试数据准备

测试数据完全模拟真实业务场景,自建两张核心业务表,提前在MySQL中生成百万级测试数据,避免空库迁移无参考价值。

用户表t_user存储用户基础信息,带JSON扩展字段,共32万行:

cpp 复制代码
-- MySQL端建表语句
CREATE TABLE t_user (
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
    username VARCHAR(64) NOT NULL COMMENT '用户名',
    age TINYINT COMMENT '年龄',
    city VARCHAR(32) COMMENT '所在城市',
    info JSON COMMENT '扩展信息',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
);

订单表t_order存储业务订单数据,覆盖分页、时间范围筛选、状态统计等高频场景,共126万行:

cpp 复制代码
-- MySQL端建表语句
CREATE TABLE t_order (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '订单ID',
    user_id INT NOT NULL COMMENT '用户ID',
    order_no VARCHAR(32) NOT NULL COMMENT '订单编号',
    amount DECIMAL(10,2) NOT NULL COMMENT '订单金额',
    status TINYINT DEFAULT 0 COMMENT '订单状态 0待支付 1已支付 2已完成',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    KEY idx_user_id (user_id),
    KEY idx_create_time (create_time)
);

数据生成用存储过程批量插入,一开始没加分批提交,32万数据跑了近10分钟,后来改成每1000条提交一次,速度提升3倍。生成后验证:t_user 共320000行,t_order 共1260000行,JSON字段、时间字段、数值字段均有随机值,覆盖业务常见查询场景。

2.3 迁移目标

这次给自己定了三个可量化的目标,不玩虚的:

  1. 数据零丢失:迁移后行数、字段值、JSON内容100%一致

  2. 语法最小改动:核心业务SQL通过兼容函数适配,尽量不改业务代码

  3. 性能不低于原库:核心查询SQL耗时优于或持平MySQL,批量写入性能差距控制在10%以内

二、金仓KES本地部署与基础环境验证

2.1 安装包获取与图形化安装实操

2.1.1 安装包下载

  1. 进入人大金仓国内官网下载中心,定位【数据库-KES-V9R1C10】,选择X64_Windows安装包下载,文件格式为ISO镜像,大小3.4G;

  2. 下载完成后,Windows10/11系统可直接双击挂载ISO镜像,老旧系统使用解压软件提取内部exe安装程序;

  1. 存放路径全程使用纯英文目录,禁止中文、空格、特殊符号。

2.1.2 标准化安装步骤

  1. 权限启动 :右键安装主程序Setup-KingbaseES-V9-Windows-x64.exe,选择以管理员身份运行,在弹窗选定简体中文,确认进入安装向导。

  2. 协议确认:勾选【我接受许可协议】,点击下一步。

  3. 安装类型选定 :选择完全安装,该模式包含数据库内核、可视化客户端、JDBC驱动、数据迁移工具、命令行工具全套组件,适配备赛开发需求;精简/典型安装会缺失驱动与工具,不推荐选用。

  4. 安装路径配置

自定义安装路径为:D:\Kingbase\ES\V9

禁止使用D:\国产数据库D:\Kingbase ES等含中文、空格的路径,此类路径会直接导致后续实例初始化失败。

开始安装 :确认快捷方式默认路径不变,点击安装,固态硬盘等待4~6分钟、机械硬盘等待7~9分钟,直至文件复制完成;安装结束后暂不勾选一键初始化,手动关闭安装窗口。

2.2 数据库实例可视化初始化

2.2.1 工具入口

两种打开方式任选其一:

  1. 开始菜单 → 人大金仓KingbaseES V9 → 数据库初始化工具;

  2. 安装目录路径:D:\Kingbase\ES\V9\bin\initdb.exe,双击运行。

2.2.2 初始化分步配置

1. 操作类型 :选择【创建新数据库实例】,实例名称固定默认KINGBASE,监听端口固定54321,不做修改。

2. 管理员账号密码设置

超级管理员用户名固定为system,密码强制满足4项规则:长度≥8位、同时包含大写字母、小写字母、阿拉伯数字、特殊符号;

合规示例:Test@2026,简单纯数字、纯字母密码会被系统拦截校验,设置完成后二次确认密码,点击下一步。

3. 字符集与兼容模式配置

数据库编码选择UTF8,排序规则默认zh_CN.UTF-8兼容模式选定MySQL兼容模式,可大幅降低后续SQL语句适配工作量。

4. 数据目录配置

数据存储路径设置为:D:\Kingbase\ES\V9\data,再次核对路径无中文、空格。

  1. 高级参数保持系统默认,点击【执行】开始初始化,耗时约2分钟。

  2. 初始化成功后,勾选【注册为Windows系统服务】,实现开机后台自启,点击完成。

2.3 服务状态核验与DBeaver连接环境验证

2.3.1 服务启停核验

1 进入D:\Kingbase\ES\V9\Server\bin,执行启动命令:

cpp 复制代码
.\sys_ctl -D "D:\Kingbase\ES\V9\data" start
  1. 出现「服务器进程已经启动」,执行状态核验:
cpp 复制代码
.\sys_ctl -D "D:\Kingbase\ES\V9\data" status
  1. 核验结果:显示PID即为正常运行,端口54321对外开放。

2.3.2 DBeaver连接配置(坑点:驱动不兼容)

PostgreSQL通用驱动不可用,必须使用人大金仓自带JDBC驱动:

  1. 新建连接,数据库类型选择KingbaseES;

  2. 基础参数

|---------|-------------|
| 参数项 | 填写内容 |
| 主机 | 127.0.0.1 |
| 端口 | 54321 |
| 数据库 | test |
| 用户名 | system |
| 密码 | 安装时设置的管理员密码 |

  1. 替换驱动为安装目录下jdbc/kingbase8.jar,测试连接,提示连通正常即可。

2.3.3 版本与兼容模式核验

  1. 打开SQL编辑器,依次执行:
cpp 复制代码
--查看版本
select version();
--查看SQL兼容模式
show sql_compatible;
  1. 判定标准
  • 版本结果包含V009R001C010

  • 兼容模式为mysql

  1. 若兼容模式不符,执行修改并重载:
cpp 复制代码
alter system set sql_compatible = 'mysql';
select pg_reload_conf();
  1. 核验无误后,创建业务库:
cpp 复制代码
CREATE DATABASE testdb;

后续开发操作统一在testdb库执行,基础部署完成。

实操避坑汇总

  1. 路径:安装、数据目录禁用中文、空格、特殊符号;

  2. 密码:管理员密码≥8位,大小写、数字、符号组合;

  3. 连接:禁用PostgreSQL驱动,仅使用KES官方JDBC;

  4. 启停:日常使用start/status/stop命令即可,无需注册系统服务。

MySQL 至人大金仓 KES 全链路迁移流程图:

四、基于KDT工具的迁移兼容性检测

迁移绝对不能上来就直接导数据,第一步必须做兼容性扫描,提前知道哪些地方要改,不然迁到一半报错返工更麻烦。金仓官方的KDT工具自带兼容性评估功能,非常实用。

4.1 KDT工具新建评估任务

打开KDT工具,操作步骤一步步来:

  1. 左上角点击「新建项目」,项目名称填mysql2kes_test,保存路径选D:\kdt_project,点击确定

  2. 左侧导航栏选择「兼容性评估」,点击「新建评估任务」,任务名称填testdb_eval

  3. 源端配置 :数据库类型选MySQL,驱动文件浏览选中本地的mysql-connector-java-8.0.36.jar,主机填127.0.0.1,端口3306,数据库名test_db,用户名root,输入MySQL密码,点击「测试连接」,提示成功后下一步

  4. 目标端配置 :数据库类型选「人大金仓」,驱动文件选金仓自带的kingbase8-9.0.3.jar,主机127.0.0.1,端口54321,数据库名testdb,用户名system,输入密码,测试连接成功后下一步

  5. 评估对象勾选t_usert_order两张表,评估选项全选:语法兼容性、数据类型映射、函数兼容性、分页语法检查、约束兼容性

  6. 点击「开始评估」,等待约1分钟出扫描报告

4.2 扫描结果分类排查

评估报告出来后,总共高风险12项、中风险37项、低风险21项。我没有逐条硬改,而是按影响范围分类排查,优先处理高风险问题:

第一类是函数不兼容 ,占了高风险项的大头,共8处。集中在三个MySQL高频函数:IFNULLSUBSTRING_INDEXDATE_FORMAT,金仓原生没有完全同名的函数,直接执行会报"函数不存在"。

第二类是分页语法差异 ,共2处高风险。MySQL的limit m,n写法金仓虽然能执行,但语义和MySQL有细微差异,大偏移量下容易出问题,属于语法高风险。

第三类是字段类型适配 ,共2处高风险、28处中风险。比如MySQL的tinyint(1)金仓默认映射为smallint,虽然能存但语义不匹配;JSON类型默认映射为文本型json,不支持索引;datetime精度和默认值行为有差异。

剩下的低风险基本是字符集排序规则、注释格式差异,不影响业务运行,可以后续慢慢处理。扫完心里就有了底:核心工作量集中在函数兼容和字段适配,分页语法只要统一改写规范,整体迁移难度并不大。

五、数据库结构适配 + 自定义兼容改造

结构适配是 MySQL 迁移人大金仓最核心的前置环节,必须先统一表结构,再迁移数据,否则数据入库后极易出现类型错乱、索引失效,还要二次整改。我全程没有完全依赖迁移工具 KDT(人大金仓迁移工具)一键自动转换,而是逐字段手动梳理适配细节,尽量贴合原有业务 SQL 编写习惯,最大限度降低后续业务代码的改动量。

5.1 字段类型手动适配改造

我整理了业务两张核心表的字段映射对照表,在 KDT 默认自动映射的基础上做精细化优化,不只要保证语法兼容,同时兼顾存储占用、查询性能与业务语义。

表格 还在加载中,请等待加载完成后再尝试复制

工具自动生成的建表语句冗余严重、类型优化不到位,因此两张核心表我全部手动改写建表 SQL 后执行部署:

cpp 复制代码
-- 金仓端用户表
CREATE TABLE t_user (
    id SERIAL PRIMARY KEY,
    username VARCHAR(64) NOT NULL,
    age SMALLINT,
    city VARCHAR(32),
    info JSONB,
    create_time TIMESTAMP(0) DEFAULT CURRENT_TIMESTAMP
);

-- 金仓订单表
CREATE TABLE t_order (
    id BIGSERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL,
    order_no VARCHAR(32) NOT NULL,
    amount NUMERIC(10,2) NOT NULL,
    status BOOLEAN DEFAULT false,
    create_time TIMESTAMP(0) DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_order_user_id ON t_order(user_id);
CREATE INDEX idx_order_create_time ON t_order(create_time);

MySQL 依靠AUTO_INCREMENT实现主键自增,人大金仓采用SERIAL/BIGSERIAL自动绑定序列实现自增,二者效果对等。 初期迁移时我忽略了序列初始值同步,存量数据导入完毕后,新插入数据频繁报主键冲突。排查后定位根源:序列默认从 1 开始,远小于存量数据最大 ID,于是使用setval()手动对齐序列当前值:

cpp 复制代码
-- 同步序列当前值为表内已有最大ID,第三个参数true代表下次自增自动+1,杜绝主键重复
SELECT setval('t_user_id_seq', (SELECT MAX(id) FROM t_user), true);
SELECT setval('t_order_id_seq', (SELECT MAX(id) FROM t_order), true);

5.2 自定义兼容函数实现

为了尽量少改业务代码,我选择在金仓端自建同名兼容函数,业务层几乎不用改SQL就能直接跑。三个高频函数逐个实现:

1. IFNULL 兼容函数

金仓原生有COALESCE函数,功能和IFNULL完全一致,简单封装一层即可:

cpp 复制代码
CREATE OR REPLACE FUNCTION ifnull(anyelement, anyelement)
RETURNS anyelement AS $$
SELECT coalesce($1, $2);
$$ LANGUAGE sql IMMUTABLE STRICT;

执行后测试:SELECT ifnull(null, 'test'); 返回test,和MySQL结果完全一致。

2. SUBSTRING_INDEX 兼容函数

这个函数费的时间最多,金仓原生只有string_to_array字符串转数组功能,需要自己实现按分隔符截取的逻辑,还要支持正负数下标。

cpp 复制代码
CREATE OR REPLACE FUNCTION substring_index(str text, delim text, count integer)
RETURNS text AS $$
DECLARE
    arr text[];
    arr_len integer;
BEGIN
    -- 空值拦截
    IF str IS NULL OR delim IS NULL OR count = 0 THEN
        RETURN '';
    END IF;
    arr := string_to_array(str, delim);
    arr_len := array_length(arr, 1);

    IF count > 0 THEN
        IF count >= arr_len THEN
            RETURN str;
        END IF;
        RETURN array_to_string(arr[1:count], delim);
    ELSE
        IF abs(count) >= arr_len THEN
            RETURN str;
        END IF;
        -- 适配PG数组闭区间切片,精准截取末尾abs(count)段
        RETURN array_to_string(arr[arr_len + count + 1 : arr_len], delim);
    END IF;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT;

写完第一次测试负数参数就出问题了:SELECT substring_index('a,b,c,d', ',', -1); 应该返回d,结果返回了a,b,c,d。调试了两次,加了raise notice打印数组长度和结束位置,发现是负数下标计算时少加了1,修正后再测,正负数场景全部和MySQL结果一致。

3. DATE_FORMAT 兼容处理

DATE_FORMAT我没有做函数封装,因为金仓的to_char功能更强、性能更好,而且业务里这类SQL数量不多,统一替换成本很低。

比如MySQL写法:SELECT DATE_FORMAT(create_time, '%Y-%m-%d') FROM t_user;

金仓改写后:SELECT to_char(create_time, 'YYYY-MM-DD') FROM t_user;

结构和函数全部改完后,我重新用KDT跑了一次兼容性扫描,高风险项全部清零,剩下的低风险项不影响业务运行,可以正式开始迁数据。

六、全量数据迁移 + 数据一致性校验

6.1 结构与数据分步迁移

迁移分两步走:先迁结构(已经手动建完了,这里主要迁索引、约束),再迁全量数据,避免边建表边插数据导致锁等待。

回到KDT工具,新建数据迁移任务:

  1. 左侧导航栏选「数据迁移」,新建迁移任务,源端和目标端配置和评估任务一致

  2. 迁移对象选择t_usert_order两张表,映射关系确认字段类型匹配

  3. 迁移选项设置:并发线程数设为4,默认是8线程,我这台机器跑8线程的话CPU直接占满,系统会卡成PPT,4线程刚好,CPU占用稳定在50%左右;批量提交大小设为1000行,避免大事务占用过多内存

  4. 勾选"跳过表结构创建",因为我们已经手动建好更优的表结构了

  5. 点击开始迁移,等待执行完成

6.2 迁移耗时实测

全程盯着进度条跑,最终实测数据:

  • t_user表:320000行,迁移耗时28秒,平均每秒约11400行

  • t_order表:1260000行,迁移耗时1分42秒,平均每秒约12300行

  • 总耗时2分10秒,百万级数据迁移速度比我预期的快很多

迁移过程中踩了个小坑:一开始开了8线程,跑到一半KDT直接闪退了,任务进度全丢。看任务日志是内存溢出,4线程下内存占用稳定在2G左右,全程很稳。

6.3 数据一致性校验

迁完不能光看成功提示,必须做一致性校验,确保数据零丢失。我做了三层校验:

  1. 行数校验 :两边分别执行SELECT COUNT(*) FROM 表名;,结果完全一致,32万和126万行不差一条

  2. 随机抽样校验:按主键随机抽取1000条数据,逐字段比对,数值、字符串、时间字段完全一致;JSON字段转成文本比对,内容完全匹配

  3. 聚合校验:分别计算订单总金额、用户平均年龄等聚合值,结果完全相同

三层校验全部通过,确认数据100%准确,没有丢失、截断、乱码问题。

6.4 轻量增量同步模拟

为了模拟生产环境不停机迁移,我开启了KDT的增量同步功能。原理是监听MySQL的binlog,把新增变更同步到金仓。

配置很简单:迁移任务完成后,点击"开启增量同步",设置同步延迟阈值。然后我写了个Python脚本,往MySQL里每秒插入10条订单数据,持续跑10分钟。

实测同步延迟稳定在1.5-2秒,没有数据丢失。对于中小业务来说,低峰期割接完全够用,停机窗口可以控制在分钟级。

七、业务SQL深度优化实战

迁移完成只是基础,性能够不够才是业务能不能切流的关键。我挑了两个业务里最常见的慢查询场景,针对性做优化,所有数据都是本地实测,不是网上抄的通用教程。

测试耗时统一用金仓的\timing on模式,每条SQL执行3次取平均值,排除缓存影响。

7.1 大偏移量深度分页优化

问题现象

订单列表页的经典分页查询,翻到第5000页以后,速度明显变慢。原始SQL和MySQL写法一致:

cpp 复制代码
SELECT * FROM t_order ORDER BY id LIMIT 20 OFFSET 100000;

优化前执行耗时:1.28秒

查看执行计划:EXPLAIN ANALYZE SELECT * FROM t_order ORDER BY id LIMIT 20 OFFSET 100000;

结果显示走了全表扫描,先扫描100020行,再丢弃前面的100000行,偏移量越大,扫描的行数越多,性能越差。

优化方案

采用"子查询定位主键 + 回表关联"的优化思路,先利用主键索引定位到起始ID,再回表查数据,避免全表扫描。

改写后SQL:

cpp 复制代码
SELECT t.* 
FROM t_order t
INNER JOIN (
    SELECT id FROM t_order ORDER BY id LIMIT 20 OFFSET 100000
) tmp ON t.id = tmp.id
ORDER BY t.id;

优化效果

改写后执行耗时:0.07秒,性能提升约18倍。

再看执行计划,内层子查询走主键索引扫描,只扫描100020条索引记录,外层回表只查20行,IO量大幅减少。

我还测试了更优的"游标分页"写法,适合有上一页下一页的场景:

cpp 复制代码
SELECT * FROM t_order WHERE id > 100000 ORDER BY id LIMIT 20;

耗时仅0.02秒,性能提升64倍。缺点是不支持跳页,适合APP端的滚动分页场景。

核心 SQL 优化前后性能对比柱状图:

7.2 JSON字段检索性能优化

问题现象

用户表按JSON里的城市字段筛选,原始查询写法:

cpp 复制代码
SELECT * FROM t_user WHERE info->>'city' = '西安';

优化前执行耗时:0.92秒

执行计划显示全表扫描32万行,逐行解析JSON字段,CPU占用很高。这也是很多人觉得国产数据库JSON性能差的原因------没用对类型和索引。

优化方案

第一步:提前把JSON字段改成了JSONB二进制类型,这是建索引的前提,普通JSON类型不支持GIN索引。

第二步:创建GIN倒排索引,专门用于JSONB字段的键值查询:

cpp 复制代码
CREATE INDEX idx_user_info_gin ON t_user USING gin(info);

第三步:改写查询语法,用@>包含运算符,才能命中GIN索引:

cpp 复制代码
SELECT * FROM t_user WHERE info @> '{"city":"西安"}'::jsonb;

这里踩了个大坑:一开始改完字段、建完索引,执行原来的->>写法,速度还是没变,执行计划还是全表扫描。翻了半天才搞明白:->>提取字段的写法不走GIN索引,必须用@>包含运算符,优化器才会选择索引。

优化效果

优化后执行耗时:0.04秒,性能提升23倍。

执行计划显示走了GIN索引扫描,只扫描符合条件的行,不用全表解析JSON。同时测试了多条件查询,比如同时匹配城市和年龄,同样走索引,性能依旧稳定。

补充测试了MySQL 8.0的JSON查询,同样数据量下耗时0.31秒,金仓JSONB+GIN索引的性能反而比MySQL快了近8倍,这是我之前没想到的。

八、金仓数据库个性化参数调优

默认参数是给低配置环境准备的,要发挥性能必须根据本地硬件调整参数。金仓的配置文件和PostgreSQL高度相似,上手成本很低。

8.1 核心配置文件路径

配置文件在实例数据目录下:D:\Kingbase\ES\V9\data\kingbase.conf

修改前建议先备份一份原文件,改崩了可以直接恢复。修改完需要重启数据库服务生效。

8.2 本地化参数定制调整

我这台机器是16G内存,不能照搬服务器的参数(比如shared_buffers设8G),必须结合本地可用内存调整。核心修改项如下:

|----------------------------|---------|-----------|------------------------------------------------------------|
| 参数名 | 默认值 | 修改后的值 | 修改原因 |
| shared_buffers | 128MB | 2GB | 共享数据缓冲区,官方建议物理内存1/8-1/4。本地系统+其他软件要占内存,设2G最稳妥,一开始试了4G直接启动失败 |
| work_mem | 4MB | 64MB | 单会话排序/哈希内存,提升order by、group by性能,避免磁盘临时文件。本地并发低,设大一点没问题 |
| maintenance_work_mem | 64MB | 256MB | 维护操作内存,建索引、vacuum更快。实测建GIN索引从12秒降到5秒 |
| effective_cache_size | 4GB | 8GB | 告诉优化器可用缓存大小,影响执行计划选择,设大一点优化器更倾向于走索引 |
| max_connections | 100 | 200 | 最大连接数,本地测试完全够用,太多会消耗内存 |
| wal_buffers | 1MB | 16MB | WAL日志缓冲区,减少磁盘IO次数,批量写入更流畅 |
| log_min_duration_statement | -1 | 1000ms | 记录超过1秒的慢SQL,方便后续优化排查 |

8.3 参数生效与性能验证

修改完配置文件,重启金仓服务:Windows服务里右键重启KingbaseES V9 - KINGBASE,或者命令行执行:

cpp 复制代码
sys_ctl restart -D D:\Kingbase\ES\V9\data

重启后验证参数:SHOW shared_buffers; 返回2GB,说明参数生效。

做了一轮简单的性能对比,调优前后对比:

  • 深度分页SQL:从0.07秒降到0.062秒,提升约11%

  • JSON查询:从0.04秒降到0.037秒,提升约7%

  • 批量插入1万条订单:从1.2秒降到0.7秒,提升约42%

  • 混合读写压测(100并发,1万次请求):TPS从320提升到480,提升约50%

整体提升非常明显,尤其是写入性能,调优后基本和MySQL持平。

九、KWR性能报告实操,自主定位数据库瓶颈

金仓自带的KWR性能报告类似Oracle的AWR,是排查数据库性能瓶颈的神器,不用额外装工具,原生支持。

9.1 KWR插件开启

一开始直接执行快照函数报错,提示函数不存在,卡了半小时。查文档才知道KWR需要手动加载插件,不是默认开启的。

开启步骤:

  1. 编辑kingbase.conf,修改参数:

    cpp 复制代码
    shared_preload_libraries = 'kwr'
  2. 重启数据库服务

  3. 连接数据库,创建扩展:

    cpp 复制代码
    CREATE EXTENSION kwr;
  4. 验证:SELECT kwr_create_snapshot(); 执行成功不报错,说明开启完成

9.2 生成性能报告实操

  1. 采集第一次快照:

    cpp 复制代码
    SELECT kwr_create_snapshot();
  2. 跑20分钟模拟业务负载:我用Python脚本跑混合读写,包含分页查询、JSON查询、订单插入、状态更新,模拟真实业务流量。

  3. 负载跑完后,采集第二次快照:

    cpp 复制代码
    SELECT kwr_create_snapshot();
  4. 查看所有快照ID确认:

    cpp 复制代码
    SELECT snap_id, snap_time FROM sys_kwr_snapshot ORDER BY snap_id;
  5. 生成HTML格式的性能报告:

    cpp 复制代码
    SELECT kwr_report(1, 2, 'html');

报告默认生成在数据目录下,打开就能看完整的性能分析。

9.3 瓶颈分析与优化

从报告里定位到三个本地环境的真实问题:

  1. 长事务占用连接 :有一个会话跑了12分钟的长事务,是我之前测试忘提交了,一直占用连接资源。解决方法:测试环境开启自动提交,长事务及时提交,异常会话用SELECT pg_terminate_backend(会话PID);杀掉。

  2. 全表扫描IO占比高:优化前的JSON查询占了40%的DB Time,全表扫描导致磁盘IO偏高。对应优化就是建GIN索引,改写SQL,优化后IO占比降到5%以下。

  3. WAL写入频繁 :小批量写入太多,导致WAL刷盘频繁。对应优化就是调大wal_buffers,业务端尽量批量提交,优化后WAL写入次数减少60%。

整个排查过程不用自己瞎猜,报告直接把TOP SQL、等待事件、IO统计列得清清楚楚,新手也能快速定位问题。

十、极简主备同步高可用测试

作为学习实践,我在本地搭了一套极简主备架构,验证金仓的流复制同步能力,不搞复杂的集群架构,主打一个轻量可复现。

轻量一主一备流复制高可用架构图:

10.1 主库配置

主库用原来的54321端口实例,先做配置调整:

  1. 创建复制专用用户:

    cpp 复制代码
    CREATE USER repl REPLICATION LOGIN PASSWORD 'Repl@2024';
  2. 编辑D:\Kingbase\ES\V9\data\pg_hba.conf,添加一行允许本地复制连接:

    cpp 复制代码
    host replication repl 127.0.0.1/32 scram-sha-256
  3. 编辑kingbase.conf,开启归档:

    cpp 复制代码
    wal_level = replica
    max_wal_senders = 10
    wal_keep_size = 1GB
  4. 重启主库服务生效

10.2 备库克隆与启动

sys_basebackup工具直接克隆主库数据,不用手动初始化,保证数据一致。

  1. 先配置系统环境变量,把D:\Kingbase\ES\V9\bin加到Path里,不然cmd里找不到命令。

  2. 管理员身份打开cmd,执行克隆命令:

    cpp 复制代码
    sys_basebackup -h 127.0.0.1 -p 54321 -U repl -D D:\Kingbase\ES\V9\data_standby -Fp -Xs -P -R
  3. 修改备库配置:编辑D:\Kingbase\ES\V9\data_standby\kingbase.conf,修改端口为54322,避免和主库冲突。

  4. 启动备库服务:

    cpp 复制代码
    sys_ctl start -D D:\Kingbase\ES\V9\data_standby

10.3 同步验证

  1. 主库执行查询,确认备库连接状态:

    cpp 复制代码
    SELECT pid, state, sync_state FROM sys_stat_replication;
  2. 数据同步测试:主库插入一条测试数据,备库查询,延迟约0.8秒,数据完全一致。

  3. 故障切换测试:停止主库服务,备库执行提升命令:

    cpp 复制代码
    sys_ctl promote -D D:\Kingbase\ES\V9\data_standby

整套轻量主备搭建下来不到半小时,对于中小业务的基础高可用需求完全够用。

十一、迁移全流程踩坑总结

整套流程跑下来,大大小小踩了十几个坑,挑11个印象最深的记录下来,都是实操才会遇到的问题,教程里一般不会写。

1. 安装路径含中文导致实例初始化失败

遇到的问题:初始化实例时直接报错退出,提示"初始化失败,错误码-1"。卡了20分钟。

排查过程:一开始以为是安装包损坏,重装了两次都不行。后来翻安装日志,在C盘用户目录的临时文件夹里找到安装日志,里面有"invalid byte sequence for encoding UTF8"的报错,才联想到路径里有中文。

解决方法:卸载重装到全英文路径D:\Kingbase\ES\V9,问题立刻解决。

2. 管理员密码复杂度不达标实例创建失败

遇到的问题:设置简单密码时,实例创建直接失败,提示密码不符合安全策略。卡了10分钟。

排查过程:一开始以为只是长度不够,加到8位纯数字还是不行。看了初始化工具的帮助文档,才知道默认强制密码策略:大小写字母+数字+特殊字符,缺一不可。

解决方法:设置符合复杂度的密码,比如Test@2024,顺利通过。

3. KDT工具连接金仓驱动不匹配

遇到的问题:KDT配置目标端时,测试连接一直报"无法创建连接,驱动类异常"。卡了30分钟。

排查过程:一开始用了通用PostgreSQL驱动,版本不兼容。又从网上下了几个金仓驱动,都不对。

解决方法:用金仓安装目录自带的驱动包D:\Kingbase\ES\V9\jdbc\kingbase8-9.0.3.jar,版本完全匹配,一次连接成功。

4. 自增主键迁移后插入报主键冲突

遇到的问题:数据迁完后,插入新数据报错"duplicate key value violates unique constraint"。卡了25分钟。

排查过程:查了表结构,自增序列还在,但是序列当前值是1,而表里已经有126万条数据了。KDT迁移数据时不会自动同步序列值。

解决方法:手动同步序列当前值为表中最大ID,SELECT setval('t_order_id_seq', (SELECT MAX(id) FROM t_order));,问题解决。

5. SUBSTRING_INDEX函数负数参数结果错误

遇到的问题:自定义函数正数参数正常,负数参数返回结果和MySQL不一致。卡了15分钟。

排查过程:在函数里加raise notice打印数组长度和截取位置,发现数组下标从1开始,负数结束位置的计算公式少加了1。

解决方法:修正结束位置计算公式为arr_len + count + 1,测试正负数场景全部正确。

6. shared_buffers设4G导致服务启动失败

遇到的问题:改完参数重启服务,提示"服务启动后立即停止",根本起不来。卡了20分钟。

排查过程:去data/sys_log目录看数据库运行日志,里面报错"could not map shared memory segment: 8",Windows下共享内存有系统限制,不能像Linux一样设很大。

解决方法:把shared_buffers从4G降到2G,服务正常启动。

7. JSON字段建GIN索引报错

遇到的问题:执行建索引语句报错"data type json has no default operator class for access method gin"。卡了40分钟。

排查过程:一开始以为是语法写错了,反复核对都没问题。查官方文档才知道,普通json类型是文本存储,不支持GIN索引,必须是jsonb二进制类型才行。

解决方法:先把字段类型改成jsonb,再建GIN索引,执行成功。

8. KWR函数不存在报错

遇到的问题:执行kwr_create_snapshot()报错"function does not exist"。卡了30分钟。

排查过程:以为是个人版不支持KWR,翻了半天官方手册,发现KWR是可选插件,需要手动加载。

解决方法:修改shared_preload_libraries参数,重启后创建kwr扩展,函数正常使用。

9. sys_basebackup命令找不到

遇到的问题:cmd里执行克隆命令,提示"不是内部或外部命令"。卡了10分钟。

排查过程:金仓的命令行工具都在bin目录下,没有自动加到系统环境变量里。

解决方法:把D:\Kingbase\ES\V9\bin加到系统Path里,重启cmd,命令正常执行。

10. 备库启动失败提示文件权限不足

遇到的问题:启动备库时报错"could not open configuration file: Permission denied"。卡了18分钟。

排查过程:克隆出来的data目录权限不对,System用户没有读写权限。

解决方法:右键data_standby文件夹→属性→安全→添加System用户,授予完全控制权限,重启服务正常。

11. KDT增量同步时闪退

遇到的问题:开8线程跑增量同步,跑十几分钟KDT就无响应闪退,进度丢失。卡了22分钟。

排查过程:开任务管理器看内存占用,KDT进程内存占了快4G,直接溢出了。本地机器内存一共16G,还要跑两个数据库实例。

解决方法:并发线程数降到4,内存占用稳定在2G左右,全程稳定不闪退。

十二、全文总结与国产化实践感悟

整套流程从零跑下来,前后花了三天业余时间,最大的感受是:国产数据库真的没有传言中那么难用、那么不堪。

之前我也和很多人一样,觉得国产数据库就是"能用但不好用",性能差、适配麻烦、出问题难查。但这次实打实从零部署、迁移、优化、搭主备跑完全程,发现只要提前做好兼容性检测,针对性做结构和函数适配,再结合业务场景调优索引和参数,金仓KES在核心业务场景的性能完全不输MySQL,甚至JSON查询这类场景表现更优。

整个过程最有价值的不是最终的性能数据,而是一步步踩坑、排查、解决的过程。从最开始安装踩路径的坑,到迁移时的语法适配,再到性能调优时的参数试错,没有一步是看一眼教程就能完美跑通的,都是自己试错、查日志、翻文档调出来的。这也是我觉得国产化学习最核心的地方:不要光看测评文章,自己动手搭一遍、迁一遍、调一遍,才能真正知道它的优缺点,心里才有底。

对于中小型业务的国产化替换,我的建议是不用畏难,完全可以先在本地测试环境跑通全流程,评估好适配工作量和性能表现,再逐步切流落地,风险可控且成本不高。后续我也会继续测试存储过程、触发器、更复杂的业务场景,积累更多国产化落地的实操经验。


相关推荐
程序猿的学习1 小时前
同一个 Key 写入两次,StarRocks 会留下什么?四种表模型讲明白了
数据库
朱峥嵘(朱髯)1 小时前
数据库如何根据全表 NDV 估算子集的 NDV
数据库·算法
ERD Online1 小时前
MySQL/Oracle/PG/SQLServer 存量库一键逆向成关系图
mysql·oracle·sqlserver
fastjson_2 小时前
Hive的介绍和使用
数据仓库·hive·hadoop
Nturmoils2 小时前
MongoDB迁移:从烟囱式部署走向 KES-AI 时代融合数据库架构
数据库
深念Y2 小时前
hotspot-adb-research
linux·数据库·adb·emmc·随身wifi·移动终端
ERD Online2 小时前
我们怎么设计 good first issue:让第一个 PR 两小时内合入
数据库·git·后端·开源·issue
NeilYuen2 小时前
Mysql实战——图片读写
数据库·mysql
Sayuanni%32 小时前
微服务_注册中心Eureka与Nacos
数据库