☰
ShardingSphere 5.4.1 适配人大金仓 8.6 的踩坑全记录
2026/10/5 4:04:19 网站建设 项目流程

这几个月一直在弄国产数据库环境的项目落地,最绕不开的一件事就是把 ShardingSphere5.4.1 接到人大金仓 8.6 上。老实讲,刚开始我以为这活儿不复杂,毕竟 ShardingSphere 从 5.3.x 开始就在 DatabaseType 里加了 KingbaseES,官方文档也写了支持金仓,可真正把驱动换掉、配置改完之后,才发现“支持”两个字背后全是细节:元数据加载失败、分页数据重复、加密字段查不到、Oracle 遗留语法解析报错,一个接一个。这篇文章就是我来来回回踩坑之后的完整记录,适合正在做金仓适配,或者准备把分库分表中间件切到国产库的团队参考。

1. 适配前的两个疑问:金仓到底兼容什么,ShardingSphere 又认什么

1.1 先摸清金仓 8.6 的“身份”

很多人在适配前根本没把数据库环境理清楚,上来就改配置,结果浪费大量时间。金仓 8.6 不是一个单一形态的数据库,它有多种兼容模式,最常见的是 PG(PostgreSQL)兼容模式和 ORA(Oracle)兼容模式,部分版本还支持 MySQL 兼容模式。这个差异直接决定了 ShardingSphere 用什么方言解析你的 SQL。

我这次适配的环境是在 PG 兼容模式下运行的,这一点很关键。你可以通过下面的 SQL 确认当前兼容模式:

SHOW database_mode;

如果返回的是pg,那么后续的 SQL 解析、元数据加载都会按 PostgreSQL 的规则走;如果返回ora,那你得额外小心,因为 ShardingSphere 的解析器可不会因为金仓开了 Oracle 兼容模式就自动变成 Oracle 方言。

另外还要确认几个基础信息:

  • 默认端口:金仓 8.6 默认端口一般是54321,而不是 PostgreSQL 的 5432。
  • 驱动类名:com.kingbase8.Driver。
  • JDBC URL 前缀:jdbc:kingbase8://。
  • 默认 schema:PG 模式下通常是public,ORA 模式下可能是sys或用户名同名 schema。

这些信息决定了你接下来的数据源配置长什么样。我见过不少同事直接把jdbc:postgresql://改成jdbc:kingbase8://、驱动换成金仓驱动就以为完事了,结果一启动就报元数据加载失败,根因往往是 schema 没对上。

1.2 ShardingSphere 是怎么识别数据库类型的

ShardingSphere 5.x 使用 SPI 机制管理数据库类型,识别方式主要有两条路径:一是根据 JDBC URL 前缀判断,二是通过Connection.getMetaData().getDatabaseProductName()拿到的产品名匹配。

在 5.4.1 中,金仓对应的是内置的KingbaseES类型。日志里一般会输出类似:

Database type: KingbaseES

如果驱动版本比较老,getDatabaseProductName()返回的可能是PostgreSQL,此时 ShardingSphere 会按 PostgreSQL 方言处理。一般也能用,因为在 PG 兼容模式下两者的 SQL 语法高度一致,但如果你在 ORA 模式下跑,又不小心写了 Oracle 特有语法,解析阶段就会出问题。

还有一个隐藏坑:如果驱动返回的产品名在 ShardingSphere 的注册表里找不到,启动时就会直接抛:

Cannot get database type from registry: unknown database type

这种报错需要用代码手动指定 DatabaseType,下面第三章会专门讲。

1.3 适配前的版本与功能清单

大版本确认好之后,我建议把适配范围圈清楚,不是所有 ShardingSphere 特性都要一步到位。这次我划定的范围是:

功能是否适配备注
单库分表是最核心场景
读写分离是与分片共存
数据加密是逻辑列与密文列映射
分布式主键是用 SNOWFLAKE,不走数据库序列
分布式事务 XA否先本地事务兜底
ShardingSphere-Proxy否前端协议兼容性风险大,见第六章

版本上我用的是 ShardingSphere 5.4.1、金仓 V8.6、JDK 8、HikariCP 连接池。如果你用 JDK 17 也没问题,但 ShardingSphere 5.4.1 在 JDK 8 上最稳。先把最小功能集跑通,再逐步放开,排查问题会容易很多。

2. 驱动替换和连接串配置:看似一分钟,实际坑不少

2.1 金仓驱动不在 Maven 中央仓库,得手动装

金仓的 JDBC 驱动kingbase8不会出现在 Maven 中央仓库里,我第一次直接用仓库坐标拉取就失败了。它通常在金仓安装目录的Interface/JDBC下,文件名类似kingbase8-8.6.0.jar。你需要把它手动安装到本地仓库:

mvn install:install-file \ -Dfile=kingbase8-8.6.0.jar \ -DgroupId=cn.com.kingbase \ -DartifactId=kingbase8 \ -Dversion=8.6.0 \ -Dpackaging=jar

装完之后在pom.xml里引入:

<dependency> <groupId>cn.com.kingbase</groupId> <artifactId>kingbase8</artifactId> <version>8.6.0</version> </dependency>

有些项目不方便装本地依赖,也可以直接把 jar 放到lib目录,用系统依赖方式引入。总之要先确认驱动真正打进了运行环境,因为后面所有连接池初化化失败,一半以上都是这个原因。

2.2 数据源 YAML 配置实例

ShardingSphere 5.4.1 用 YAML 配置数据源时,我建议坚持用 HikariCP 作为连接池。下面这份配置是经过实测可用的:

dataSources: ds_0: dataSourceClassName: com.zaxxer.hikari.HikariDataSource driverClassName: com.kingbase8.Driver jdbcUrl: jdbc:kingbase8://127.0.0.1:54321/demo?currentSchema=public&useUnicode=true&characterEncoding=UTF-8 username: system password: xxxxxx maximumPoolSize: 50 minimumIdle: 5 connectionTimeout: 30000 idleTimeout: 600000 rules: - !SHARDING tables: t_order: actualDataNodes: ds_0.t_order_$->{0..3} tableStrategy: standard: shardingColumn: order_id shardingAlgorithmName: t_order_inline keyGenerateStrategy: column: order_id keyGeneratorName: snowflake shardingAlgorithms: t_order_inline: type: INLINE props: algorithm-expression: t_order_$->{order_id % 4} keyGenerators: snowflake: type: SNOWFLAKE props: worker-id: 1 props: sql-show: true

有几个细节要注意:

  • driverClassName一定要写全,有些版本的 HikariCP 可以根据 URL 自动推断,但在金仓驱动上偶尔推断不出来,宁可显式写上。
  • currentSchema=public这个参数建议无条件加上,它能避免很多元数据加载和 schema 找不到的诡异问题。
  • sql-show: true必须开,排查问题全靠它看逻辑 SQL 和真实下发 SQL。

2.3 连接池初始化失败的几个典型报错

这部分我直接给出报错和排查方向,帮你少走弯路:

报错信息实际原因处理方式
Failed to load driver class com.kingbase8.Driver驱动 jar 没打进去检查依赖和打包产物,确认 lib 里有驱动
Connection is not available, request timed out连接串配错或端口不通telnet 127.0.0.1 54321先测连通性
The connection attempt failedURL 或用户密码问题先用 DBeaver 等工具直接连,排除库端问题
Cannot load driver class: com.kingbase.Driver驱动类名写错确认是com.kingbase8.Driver,不是com.kingbase.Driver

从实践来看,连接池这层问题最不值得花时间,优先用客户端工具证明“JDBC 能连通”,再回头检查 ShardingSphere 配置,问题一下子就定位了。

3. 元数据加载失败:ShardingSphere 启动阶段的头号拦路虎

3.1 元数据加载在启动时到底做了什么

ShardingSphere 5.x 在初始化分片、加密等规则时,会主动加载真实表的元数据,包括表结构、列名、主键、索引等。它通过 JDBC 的DatabaseMetaData接口实现,也就是说需要执行一系列基于information_schema和pg_catalog的查询。

金仓 8.6 在 PG 兼容模式下,information_schema和pg_catalog基本是兼容的,大部分查询能正常返回。但有一个前置条件:连接的用户必须有权限访问目标 schema,且currentSchema指向正确。如果currentSchema没配,而 ShardingSphere 又拿不到默认 schema,它可能会遍历所有 schema 去找表,轻则加载慢,重则看到重复表名直接报冲突。

3.2 schema 与 currentSchema 的真实坑

我这次遇到的现象是:启动日志里能看到加载数据源成功,但随后报:

Table 't_order' does not exist in database 'demo'

诡异的是表明明建了。后面查下来,问题出在Connection.getSchema()返回的是null,ShardingSphere 无法确认表所在的 schema,最终在元数据加载阶段把表过滤掉了。

解决方式就是在 JDBC URL 上显式指定 schema:

jdbc:kingbase8://127.0.0.1:54321/demo?currentSchema=public

如果你的表建在sys或app等下,就把public替换成对应 schema。这一步对于金仓 8.6 来说非常关键,建议直接当标配写进连接串。

另外还有一个不容易注意的点:金仓某些版本的主键元数据需要通过pg_index获取,如果业务表建成了无主键堆表,ShardingSphere 加载到的主键信息就是空的。这会影响后续基于主键的 SQL 改写和去重逻辑。适配前最好给业务表补上主键,哪怕是用雪花算法生成的分布式主键,也需要在真实表上建立物理主键约束。

3.3 手动指定 DatabaseType 的兜底办法

正常情况下,ShardingSphere 5.4.1 能通过驱动自动识别金仓为KingbaseES,日志里也会打印对应的数据库类型。但如果你用的是旧版金仓驱动,或者驱动对getDatabaseProductName()的实现有偏差,识别就可能失败。

手动指定数据库类型的做法是:用ShardingSphereDataSourceFactory.createDataSource重载方法,在代码里显式传入DatabaseType:

DataSource dataSource = ShardingSphereDataSourceFactory.createDataSource( dataSourceMap, ruleConfigs, new Properties(), DatabaseTypeEngine.getDatabaseType("KingbaseES"));

这里有一点需要提醒:5.4.1 源码里DatabaseTypeEngine的包名在不同小版本中可能有调整,具体类路径以你本地 jar 为准。不同版本的字符串值也可能是KINGBASE或KingbaseES,建议写个小的单元测试先打出来确认,避免编译过了运行时报No such database type。

如果是在纯 YAML 环境下没法改 Java 代码,那就优先升级驱动的兼容版本,让自动识别正常工作。手动指定是兜底,不该是首选。

4. SQL 解析与改写:分页、分片键和加密下推的实测记录

4.1 解析层:为什么 PG 解析器能顶半个金仓

ShardingSphere 5.4.1 的 SQL 解析是分层的:先用对应方言的解析器把 SQL 解析成 AST,再统一标准化成内部逻辑 SQL,最后根据分片、加密等规则改写。

金仓在 PG 兼容模式下,CRUD 语法与 PostgreSQL 高度一致,所以 ShardingSphere 内置的 PostgreSQL 方言解析器几乎可以“白嫖”。实际适配中大量问题不出在解析层,而出在业务 SQL 里残留的 Oracle 风格语法。

最典型的就是ROWNUM。比如下面这条 SQL,在金仓 ORA 模式下能执行:

SELECT * FROM t_order WHERE ROWNUM <= 10;

但在 ShardingSphere 里走到 PostgreSQL 方言解析器时,会直接报:

SQL parse error. You may have an error in this SQL statement: non support token 'ROWNUM'

类似的还有(+)外连接、START WITH ... CONNECT BY层次查询、SYSDATE等。凡是这类 Oracle 专属写法,在金仓适配 ShardingSphere 的场景下统统不建议留。处理方式很简单,统一改成 PG 风格:

SELECT * FROM t_order LIMIT 10;

我建议在适配阶段做一轮 SQL 合规扫描,把所有ROWNUM、NVL、SYSDATE等 Oracle 痕迹清掉,省得后面上线时被用户的一条临时查询打挂。

4.2 分页改写:LIMIT 与 FETCH FIRST 的兼容细节

分页是分片场景下改写最频繁的逻辑。ShardingSphere 会对分页 SQL 做改写,必要时还要在内存里做二次归并。比如你要查第 2 页、每页 20 条,实际下发到每个分片的 SQL 会变成取前 40 条再归并截断。

这种机制对金仓本身是透明的,因为 PG 模式支持标准的LIMIT ? OFFSET ?语法,PreparedStatement 的参数绑定也正常。但有两个细节值得注意:

  • 如果业务 SQL 用了FETCH FIRST ? ROWS ONLY这种写法,需要确认金仓驱动支持预编译参数绑定。实测中金仓 8.6 对FETCH FIRST的动态参数支持偶尔会有兼容问题,报错类似于syntax error at or near "?"。稳妥起见,统一改写成LIMIT ? OFFSET ?。
  • 分片键上的分页查询,如果ORDER BY字段不是分片键,ShardingSphere 会把排序下推到每个分片执行,再内存归并。这个过程中如果ORDER BY字段本身是加密逻辑列,改写后的真实列名可能跟原始排序条件对不上,出现“列不存在”的报错。处理办法是让加密字段只作为数据存储列,不作为排序和查询条件列使用。

4.3 加密字段下推:列名大小写是金仓最容易踩的坑

数据加密是 ShardingSphere 的高频功能。配置上,你需要定义逻辑列、密文列、明文列和加密算法。下面是我实际用过的最小配置:

- !ENCRYPT encryptors: aes_encryptor: type: AES props: aes-key-value: 1234567890abcdef tables: t_user: columns: phone: cipherColumn: phone_cipher plainColumn: phone_plain encryptorName: aes_encryptor

写入时,业务 SQL 只需要关心逻辑列phone,ShardingSphere 会自动改写为INSERT INTO t_user (phone_cipher, phone_plain),并把明文加密后写入密文列。

这个机制本身不区分数据库品牌,但在金仓上我遇到一个很实际的问题:大小写敏感。PG 兼容模式对未加引号的标识符一律折叠为小写。如果你的建表 DDL 写的是:

CREATE TABLE t_user ("Phone_Cipher" VARCHAR(100), "Phone_Plain" VARCHAR(100));

那么 ShardingSphere 改写出来的列名是phone_cipher,双双对不上,报错:

column "phone_cipher" does not exist

解决办法很粗暴:建表时所有表名、列名一律小写,不要加双引号。如果你被历史遗留的“驼峰加引号”表结构绑住,那只能在加密规则里把cipherColumn配成和物理列完全一致的大小写,并在 SQL 里也带引号访问,但这种做法的维护成本很高,不推荐。

4.4 分片键路由的非分片键查询代价

分片场景下还有一个容易忽略的问题:如果你按order_id分片,但查询条件只用user_id,ShardingSphere 会把这个查询广播到所有分片,再合并结果。这个过程对金仓是透明的,但性能会随分片数量线性下降。我建议在 SHARDING 规则里配置绑定表:

bindingTables: - t_order,t_order_item

哪怕是一对多的关联查询,只要分片键一致,就可以避免笛卡尔积式的跨分片关联。这个跟数据库品牌无关,但适配金仓时尤其值得做,因为金仓在并发处理上不如 PostgreSQL 原生环境宽裕,少一些广播查询,压力能小不少。

5. 一次分页查询错乱问题的完整排查链路

5.1 现象:第一页正常,第二页出现第一页的数据

适配过程中我遇到一个很经典的坑,值得单独写一节。现象是:t_order按order_id % 4分片,业务查询时第一页数据正常,翻到第二页时,出现了第一页已经出现过的数据。

第一反应是分页改写参数错了,但连着检查好几遍 SQL,都没发现逻辑问题。后来把所有相关日志打开,才发现问题不在 ShardingSphere,而在金仓驱动的连接参数。

5.2 排查过程:从 SQL 日志到 JDBC 驱动参数

第一步,打开 ShardingSphere 的 SQL 日志。配置:

props: sql-show: true

日志里会输出Logic SQL和Actual SQL。我看到逻辑 SQL 是:

SELECT * FROM t_order ORDER BY create_time LIMIT 20 OFFSET 20;

而实际下发的 SQL 也是:

SELECT * FROM t_order_0 ORDER BY create_time LIMIT 20 OFFSET 20; SELECT * FROM t_order_1 ORDER BY create_time LIMIT 20 OFFSET 20;

参数完全正确,所以第一层排除 ShardingSphere 改写问题。

第二步,检查金仓是否真的按OFFSET 20返回。我用金仓客户端手工执行同样的 SQL,结果也正确。于是问题收敛到 JDBC 驱动层。

第三步,排查连接串参数。我发现初始化时为了优化大结果集,连接串里加了:

useCursorFetch=true&defaultRowFetchSize=100

这个参数在 PostgreSQL JDBC 里用于开启游标分批取数。PG JDBC 在使用游标模式下,如果事务处于自动提交,setFetchSize不会生效,但金仓驱动的部分版本对这种组合的处理有瑕疵,会导致分页查询在内存和游标之间串页,最终出现翻页重复。

5.3 根因与修复方案

根因就是useCursorFetch=true和 ShardingSphere 分页归并机制叠加后,游标取数把“偏移量”重复计算了。修复方案很简单:金仓 8.6 环境下,去掉useCursorFetch和defaultRowFetchSize,让分页数据一次性进入 ShardingSphere 内存归并。

修改后的连接串:

jdbc:kingbase8://127.0.0.1:54321/demo?currentSchema=public&useUnicode=true&characterEncoding=UTF-8

改完之后连续翻页 30 轮,没有重复也没有漏行。这个坑给我一个教训:从 PostgreSQL 迁移到金仓时,连接串参数不能原样照搬。金仓驱动虽然内核沿袭了 PG,但游标取数这类行为差异很大,最好在生产压测前逐项验证。

5.4 排查链路的通用价值

这套排查链路其实适用于所有“ShardingSphere + 国产数据库”的诡异查询问题:

  1. 先看Logic SQL和Actual SQL,确认中间件改写是否正确。
  2. 再用数据库客户端手工执行Actual SQL,确认数据库本身返回是否正确。
  3. 最后检查 JDBC 连接参数和驱动版本,重点怀疑与 fetchSize、游标、事务隔离相关的参数。

按这个顺序走,大部分问题能在半小时内定位。反过来,如果你一上来就怀疑 ShardingSphere 的 SQL 改写,很容易陷入反复翻源码的泥潭,浪费时间。

6. 事务模式和 Proxy 选型:适配方案的边界在哪儿

6.1 本地事务与 XA 在金仓上的表现

ShardingSphere 5.4.1 支持三种事务模式:本地事务、XA 事务和 BASE 事务。

本地事务是最稳的选择。金仓 8.6 在 PG 兼容模式下,单库单事务完全没问题,ShardingSphere 的多分片写入在本地事务下会根据路由结果顺序提交,只要业务对一致性要求不跨分片,这个模式完全够用。

XA 事务理论上支持,但要满足两个前提:一是金仓驱动实现了javax.sql.XADataSource接口,二是应用里引入 Atomikos 或 Narayana 的事务管理器。我在测试中发现,金仓 8.6 驱动能做 XA 注册,但和 Atomikos 的日志目录权限、连接池代理对象之间存在较多组合问题。如果你对金仓 XA 不是特别熟,我更建议用 Seata 这类 BASE 方案,或者干脆在业务层做本地消息表补偿,别把分布式事务这个重担压给数据库适配层。

如果你确实要用 XA,测试时可以从简单场景开始:

TransactionTypeHolder.set(TransactionType.XA);

然后只做“两个分片同时写入”的冒烟测试,观察事务管理器日志里是否出现XAER_RMERR或XAER_RMFAIL。出现这类错误基本可以确认驱动和事务管理器不兼容,趁早换方案。

6.2 ShardingSphere-JDBC 与 Proxy 的现实选择

关于 ShardingSphere-Proxy,这里要给一个比较直接的建议:在人大金仓 8.6 的适配初期,优先选 ShardingSphere-JDBC 嵌入式模式,不要一上来就上 Proxy。

原因在于 Proxy 是一个独立数据库中间件进程,客户端需要以对应的数据库协议连接它。ShardingSphere-Proxy 5.4.1 对外主要支持 MySQL 和 PostgreSQL 协议,金仓的自有客户端协议并非标准 PG wire protocol。如果金仓侧没有开启 PG 兼容协议端口,你的应用直接用金仓驱动连 Proxy,大概率会握手失败或者认证失败。

接入场景推荐方案原因
单个 Java 应用需要分片加密ShardingSphere-JDBC嵌入式无协议问题,排查简单
多个应用共享同一套分片规则先 JDBC + 配置中心,再评估 Proxy多应用一致性问题优先靠配置管理解决
非 Java 应用需要分片能力Proxy + 金仓 PG 兼容协议端口需要提前验证协议连通性,风险较高
要求全链路透明免改造不推荐 ShardingSphere建议评估数据库内置分布式方案

如果你的团队确实需要 Proxy,我的建议是先确认金仓 8.6 能不能开启兼容 PG 协议的对外端口,然后写一个最小客户端做一次连接测试,通过后再谈 Proxy 部署。

6.3 主键策略:别让 sequence 成为瓶颈

分片场景下,主键生成是经常被遗忘的一环。许多业务从 Oracle 迁到金仓时习惯用SERIAL或NEXTVAL('seq_xxx'),但在 ShardingSphere 分片架构里,单库 sequence 生成的全局唯一 ID 会冲突,多库 sequence 又很难保证顺序一致。

更合适的方式是使用 ShardingSphere 的分布式主键生成器。我的推荐是 SNOWFLAKE:

keyGenerators: snowflake: type: SNOWFLAKE props: worker-id: 2

配套的表结构里,主键列用BIGINT,和雪花算法返回的 long 完全匹配。如果你的表已经用了 UUID 字符串主键,那就把策略配成:

keyGenerators: uuid: type: UUID

并保证主键列是VARCHAR(36)。实测中,SNOWFLAKE 的性能和稳定性比 UUID 好,尤其在金仓这种对字符串索引不占优势的数据库上,能优先用雪花就用雪花。

另外要留意:ShardingSphere 的 SNOWFLAKE 默认会生成带时间戳的长整型,如果你下游系统对 ID 长度有严格限制,比如只有 MySQL 的BIGINT无符号范围,那也问题不大。但如果对接的老系统用了INT类型主键,雪花 ID 会溢出,这时候只能改用 UUID,没有别的选择。

6.4 适配完成后的快速冒烟验证清单

最后分享一个我每次适配新库都会跑的冒烟链路,顺手整理成清单。你在金仓 8.6 上做完基本配置后,按下面的顺序跑一遍,能覆盖绝大多数核心风险点:

  1. 通过 ShardingSphere 执行一条INSERT,确认分片路由和主键生成正常。
  2. 按分片键执行SELECT,确认能定位到正确分片。
  3. 按非分片键执行SELECT,确认广播查询能合并结果。
  4. 连续翻三页分页数据,确认无重复无漏行。
  5. 对加密字段执行一次写入和查询,确认密文列、明文列读写正常。
  6. 查看日志中的Actual SQL,确认 schema 前缀、列名大小写都符合预期。

跑完这条链路,适配的核心风险基本都覆盖到了,剩下就是等压测暴露性能问题。我在实际项目里的体会是,金仓 8.6 的兼容性并没有很多文章写得那么可怕,真正让人头疼的都是环境细节:schema 没对上、驱动参数照搬、历史遗留 SQL 不兼容。只要把这些细节当成一等公民对待,ShardingSphere5.4.1 接人大金仓8.6这套组合是能稳定跑下去的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询