KingbaseES:兼容模式、JDBC 与可恢复实验
一、是什么
从“能连上”走到“可以交付”
KingbaseES 在研发环境里也容易被写偏:有人只知道它默认端口 54321,却不知道 Docker 镜像是 tar 包导入;有人把系统账号或文档示例密码写进项目;有人把 DB_MODE=oracle 当成运行时开关,建完表再想切 PostgreSQL 或 MySQL;有人把 Docker 精简镜像当成包含所有客户端和驱动的完整安装包,结果 JDBC、ksql、license、encoding 和 volume 都没有写清。
真正的适配证据不是一条 select 1,而是版本与补丁可追踪、初始化参数一致、应用账号最小授权、迁移脚本可重复执行、关键 SQL 语义经过正反验证、备份能够恢复,并且团队知道何时应该停用开发容器,进入 RAC、RWC、备份平台或正式容灾方案的评审。
KingbaseES 也不能简单视作“换了端口的 PostgreSQL”或“兼容 Oracle 的数据库”。兼容模式会改变初始化后的 SQL 语义,驱动和数据库补丁又分别迭代;一个在开发机能工作的 SQL、JDBC 驱动或 ORM 方言,未必能在目标版本和目标模式中保持相同行为。
准备一套可丢弃的验证环境
第一次操作前准备:
KingbaseES 官方下载账号或邮箱验证码,按项目需要下载安装包、Docker 镜像 tar 和 JDBC 驱动。Docker / Compose,或一台符合 KingbaseES 安装要求的 Linux / Windows 开发机。预留端口 54321,本机已有 KingbaseES 或共享环境时映射为 154321:54321。
至少一种客户端:ksql、DBeaver、DataGrip、应用 JDBC 或企业内部工具。一个强密码占位符,例如 YOUR_STRONG_KINGBASE_PASSWORD,真实值只放本机 .env 或密钥系统。一套明确标记为开发或验证环境的主机、账号和数据。试用授权、开发 Compose 与本地 volume 都不能直接承担生产数据。
先检查端口:
docker version
docker compose version
lsof -i :54321Windows:
netstat -ano | findstr ":54321"KingbaseES Docker 镜像不是公共仓库里随手拉取的通用镜像,而是从产品下载中心取得 tar 包后导入。下载页把 ISO 描述为包含数据库主体、接口驱动和工具的全量包,把 Docker 镜像描述为只含数据库主体的精简 tar 包;数据库、接口驱动和工具的补丁还会分别迭代。因此环境清单至少要记录数据库版本与补丁、驱动版本、镜像文件哈希、license owner 和到期处置人。
金仓已经发布 V9 V009R001C010 产品线;下文容器参数仍以官方 V8 Docker 手册展示的镜像行为为可执行验证基线,不代表任意 V8/V9 构建都能混用同一镜像、驱动或补丁。下载到介质后先记录 docker image inspect 结果、数据库 SELECT version() 输出和 JDBC 驱动版本;项目目标若是 V9 或指定补丁线,应回到对应版本手册重新核对参数和兼容矩阵,不能因为产品名相同就沿用 V8 示例。
冻结版本和初始化参数
Docker 安装手册给出的容器内默认端口是 54321,DB_USER、DB_PASSWORD、DB_MODE、ENCODING 等变量在首次初始化时参与创建数据库。DB_MODE 对应 initdb --dbmode 的初始化选择;已有数据目录会保留旧状态,修改 Compose 环境变量并不会把现有库转换成另一种模式。
首次启动前冻结这些字段:
| 字段 | 决定什么 | 修改后的代价 |
|---|---|---|
| 数据库版本与补丁 | SQL 行为、安全修复、系统目录和工具兼容 | 需要升级验证与回退包 |
| JDBC 驱动版本 | URL 解析、类型映射、连接与协议兼容 | 需要应用回归和连接池验证 |
DB_MODE | Oracle、PG、MySQL 等兼容语义 | 通常应重建并迁移,不能原地当开关 |
ENCODING / locale | 字符存储、排序、大小写与客户端转换 | 建库后修改成本高,可能要导出导入 |
| timezone | 时间显示、默认值与跨系统比对 | 需要数据库、JVM、业务字段统一 |
| 数据目录与 license 路径 | 数据和授权能否跨容器重建保留 | 路径错会表现为“新库”或授权失效 |
下载页或手册发生变化时,应在升级工单中重新核对包类型、哈希、补丁关系、支持平台和授权条款;这些事实影响的是可部署性,不应靠旧环境截图推断。
二、为什么
从兼容目标、介质边界和故障责任选择形态
比较主流部署方式
| 方式 | 定义 | 适用场景 | 不适合 |
|---|---|---|---|
| 官方 Docker tar | 下载官方 tar,docker load 后运行 | 本地学习、CI smoke、短期联调 | 未授权分发、生产 |
| Linux / Windows 安装包 | 使用官方安装介质完整安装 | 固定开发机、工具链完整体验 | 快速清理、多版本并存 |
| 共享开发实例 | DBA 或平台团队提供的非生产 KingbaseES | 多人联调、权限验证、兼容模式验证 | 个人 reset、随意改初始化模式 |
| 企业非生产实例 | 与生产版本接近的测试库 | 上线前验证、迁移演练 | 替代生产 runbook |
| 云化或托管能力 | 云或企业服务提供的 KingbaseES 能力 | 云上联调、托管运维体验 | 直接把开发 Docker 命令用于生产 |
选型建议:
个人学习:官方 Docker tar 或完整安装包。团队联调:共享开发实例,明确 DB_MODE、encoding、license、账号和 owner。兼容验证:按目标应用选择 Oracle / PG / MySQL 模式,在初始化前定稿。
生产兼容验证:使用与生产同版本、同补丁、同初始化参数的非生产实例。生产部署:由 DBA 和平台团队按容量、高可用、备份与安全要求实施,不复制开发 Compose。
三、怎么做
固定可追溯的开发实例
从 Docker tar 启动单容器
官方手册的核心流程是先导入 tar:
docker load -i kingbase.tar
docker images学习环境运行示例:
docker run -tid --privileged \
-p 54321:54321 \
-v /tmp/kingbase-data:/home/kingbase/userdata/ \
-e DB_USER=kingbase \
-e DB_PASSWORD=YOUR_STRONG_KINGBASE_PASSWORD \
-e DB_MODE=oracle \
-e ENCODING=utf8 \
--name kingbase \
kingbase:v1 \
/usr/sbin/init说明:
kingbase:v1 是导入后的本地镜像名示例,实际以 docker images 为准。DB_USER / DB_PASSWORD 必须显式设置,不把手册示例密码写进项目。DB_MODE 是初始化模式,不能建完表后随便切。
ENCODING 建议显式设置并用 SQL 验证。volume 目录权限不足会导致 Permission denied。
用 Compose 固定初始化契约
Compose 是由官方 docker run 参数改写的本地学习模板,不是官方通用模板:
services:
kingbase:
image: "${KINGBASE_IMAGE}"
container_name: tool-kingbase-dev
privileged: true
environment:
DB_USER: "kingbase"
DB_PASSWORD: "${KINGBASE_PASSWORD}"
DB_MODE: "oracle"
ENCODING: "utf8"
NEED_START: "true"
ports:
- "54321:54321"
volumes:
- kingbase-data:/home/kingbase/userdata/
healthcheck:
test:
[
"CMD-SHELL",
"ksql -Ukingbase -d test -c 'select 1;' >/dev/null 2>&1"
]
interval: 20s
timeout: 10s
retries: 30
start_period: 90s
volumes:
kingbase-data:.env.example:
KINGBASE_IMAGE=kingbase:v1
KINGBASE_PASSWORD=YOUR_STRONG_KINGBASE_PASSWORD
KINGBASE_APP_PASSWORD=YOUR_STRONG_KINGBASE_APP_PASSWORD
KINGBASE_RUNTIME_PASSWORD=YOUR_STRONG_KINGBASE_RUNTIME_PASSWORD团队模板必须补充:
tar 包来源、下载日期、版本、试用授权和 owner。镜像 tag / digest。DB_MODE、ENCODING、locale、timezone。
客户端和 JDBC 驱动获取方式。volume、license、配置文件持久化路径。
建立 database、schema 与驱动边界
用业务用户承载 schema 和对象
KingbaseES 支持 database,但兼容模式会影响对象、类型和 SQL 行为。学习环境可以用:
CREATE USER app_tool_efficiency WITH PASSWORD 'YOUR_STRONG_KINGBASE_APP_PASSWORD';
CREATE DATABASE app_tool_efficiency OWNER app_tool_efficiency;连接目标库后:
CREATE SCHEMA IF NOT EXISTS app AUTHORIZATION app_tool_efficiency;
ALTER USER app_tool_efficiency SET search_path = app, public;原则:
应用不使用 system 或安装管理账号。runtime / migration / readonly 账号分离。共享实例中每个项目独立 database 或 schema。
初始化模式和 encoding 先定,再建业务对象。search_path 写进初始化脚本和项目文档。
共享环境把建表与运行权限拆开。迁移账号拥有目标 schema 的 DDL,运行账号只获得业务所需的 DML,只读账号只获得查询权限。授权语句要按目标版本验证,不能直接给所有账号管理员角色。完成授权后,用运行账号故意执行一条 DROP TABLE:预期应返回权限拒绝;如果成功,说明最小权限没有真正落地。
还要验证新对象的默认权限。只给现有表执行一次 GRANT,后续迁移新建的表可能对运行账号不可见;由 schema owner 配置默认权限,或让迁移流程在每次建表后显式授权,并在 CI smoke 中用运行账号访问最新对象。
在初始化前确定兼容模式
DB_MODE / initdb --dbmode 是初始化模式,常见取舍:
| 模式 | 适用 | 风险 |
|---|---|---|
oracle | Oracle 迁移、PL/SQL 风格、空串语义验证 | 不能按 PostgreSQL 行为猜 |
pg | PostgreSQL 生态和工具接近 | Oracle 空串兼容参数不要乱开 |
mysql | MySQL 迁移样例验证 | 类型、函数、大小写仍需逐项验证 |
模式不是万能兼容开关。类型、空串、大小写、日期、序列、自增、函数、分页和锁行为都要用项目样例验证。
让 JDBC 驱动跟随产品大版本
V8 与 V9 的驱动别名不能混写。V8 Docker 样例继续使用 kingbase8;V9 常见问题手册给出的驱动类和 URL scheme 是 kingbase86:
| 产品线 | URL | Driver class | 验证要求 |
|---|---|---|---|
| V8 示例基线 | jdbc:kingbase8://host:54321/database | com.kingbase8.Driver | 与目标 V8 补丁和 JDK 回归 |
| V9 | jdbc:kingbase86://host:54321/database | com.kingbase86.Driver | 使用 V9 对应驱动,不复用 V8 jar 推断 |
下文 V8 容器基线的 JDBC 配置是:
spring.datasource.url=jdbc:kingbase8://localhost:54321/app_tool_efficiency
spring.datasource.username=app_tool_runtime
spring.datasource.password=YOUR_STRONG_KINGBASE_RUNTIME_PASSWORD
spring.datasource.driver-class-name=com.kingbase8.Driver如果密码或连接参数包含 %、?、&、/ 等特殊字符,优先使用 Properties 或框架的独立 username/password 字段,不把所有参数拼进 URL。
贯通 USER_DEMO、事务与隔离恢复
启动后证明本地与 TCP 不是同一层
导入并启动:
docker load -i kingbase.tar
docker run -tid --privileged -p 54321:54321 \
-v /tmp/kingbase-data:/home/kingbase/userdata/ \
-e DB_USER=kingbase \
-e DB_PASSWORD=YOUR_STRONG_KINGBASE_PASSWORD \
-e DB_MODE=oracle \
--name kingbase kingbase:v1 /usr/sbin/init检查日志和状态:
docker logs kingbase --tail 160
docker exec -it kingbase sys_ctl -D /home/kingbase/userdata/data status先用镜像自带的 ksql 走容器内本地连接。官方 Docker 初始化日志会提示本地连接采用 trust,因此这一步不把密码写进命令行;它只证明数据库进程和容器内客户端可用,不证明宿主机 TCP 认证已经通过:
docker exec kingbase ksql -Ukingbase -d test -c "select 1;"连接成功时应返回一行 1。随后再用完整安装介质中的宿主机 ksql、JDBC 或项目应用验证 127.0.0.1:54321 的 TCP 路径;远程连接会按 sys_hba.conf 要求提示密码。如果出现 connection refused,先看容器状态和端口映射;如果出现认证失败,确认 DB_USER、密码和已有 volume 是否来自同一次初始化。不要在认证失败时改用管理账号绕过问题,也不要把密码拼进命令参数。
创建业务对象并证明事务可以回滚
SQL 验证:
SELECT version();
SHOW server_encoding;
SHOW timezone;
CREATE USER app_tool_efficiency WITH PASSWORD 'YOUR_STRONG_KINGBASE_APP_PASSWORD';
CREATE DATABASE app_tool_efficiency OWNER app_tool_efficiency;连接业务库后:
CREATE SCHEMA IF NOT EXISTS app AUTHORIZATION app_tool_efficiency;
SET search_path TO app, public;
CREATE TABLE user_demo (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(64) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
INSERT INTO user_demo(username) VALUES ('demo');
SELECT id, username, created_at FROM user_demo ORDER BY id;预期能看到 demo 这一行。随后做一次事务回滚,证明连接不只是能执行自动提交:
BEGIN;
INSERT INTO user_demo(username) VALUES ('should_rollback');
SELECT count(*) FROM user_demo WHERE username = 'should_rollback';
ROLLBACK;
SELECT count(*) FROM user_demo WHERE username = 'should_rollback';事务内第一次计数应为 1,回滚后应为 0。如果回滚后仍为 1,要检查客户端是否拆分了连接、是否启用了自动提交,或 SQL 是否实际进入了另一个 schema。
用 runtime 身份完成 JDBC 写读并拒绝 DDL
前面的 app_tool_efficiency 是 database/schema owner,适合迁移,不应直接成为运行时身份。由管理账号在业务库创建 runtime 用户并只授予现有业务表的 DML;默认权限要由 schema owner 按目标版本语法配置,保证后续迁移新建对象也能被 runtime 使用:
CREATE USER app_tool_runtime WITH PASSWORD 'YOUR_STRONG_KINGBASE_RUNTIME_PASSWORD';
GRANT CONNECT ON DATABASE app_tool_efficiency TO app_tool_runtime;
GRANT USAGE ON SCHEMA app TO app_tool_runtime;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_tool_runtime;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA app TO app_tool_runtime;
ALTER USER app_tool_runtime SET search_path = app, public;
ALTER DEFAULT PRIVILEGES FOR USER app_tool_efficiency IN SCHEMA app
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_tool_runtime;
ALTER DEFAULT PRIVILEGES FOR USER app_tool_efficiency IN SCHEMA app
GRANT USAGE, SELECT ON SEQUENCES TO app_tool_runtime;用 runtime 用户连接后,SELECT 与 INSERT 应成功,而下面的 DDL 必须被拒绝:
SET search_path TO app, public;
SELECT id, username FROM app.user_demo ORDER BY id;
INSERT INTO app.user_demo(username) VALUES ('runtime-smoke');
DELETE FROM app.user_demo WHERE username = 'runtime-smoke';
CREATE TABLE app.runtime_should_fail(id INTEGER);若最后一条意外成功,立即由 owner 删除 app.runtime_should_fail,回收 runtime 的 schema CREATE 或过大角色,再重新连接复测。JDBC 探针继续使用同一 runtime 身份和 USER_DEMO:
import java.sql.*;
public final class KingbaseSmoke {
public static void main(String[] args) throws Exception {
String url = System.getenv("KINGBASE_JDBC_URL");
String user = System.getenv("KINGBASE_RUNTIME_USER");
String password = System.getenv("KINGBASE_RUNTIME_PASSWORD");
if (url == null || user == null || password == null) {
throw new IllegalStateException("缺少 KINGBASE_JDBC_URL、KINGBASE_RUNTIME_USER 或 KINGBASE_RUNTIME_PASSWORD");
}
try (Connection connection = DriverManager.getConnection(url, user, password)) {
connection.setAutoCommit(false);
try {
try (Statement identity = connection.createStatement();
ResultSet result = identity.executeQuery(
"SELECT current_database(), current_user, current_schema()")) {
if (!result.next()
|| !"app_tool_efficiency".equalsIgnoreCase(result.getString(1))
|| !"app_tool_runtime".equalsIgnoreCase(result.getString(2))) {
throw new SQLException("database/runtime 身份与预期不符");
}
}
try (PreparedStatement insert = connection.prepareStatement(
"INSERT INTO app.user_demo(username) VALUES (?)")) {
insert.setString(1, "jdbc-smoke");
if (insert.executeUpdate() != 1) throw new SQLException("canary 写入行数异常");
}
try (PreparedStatement count = connection.prepareStatement(
"SELECT COUNT(*) FROM app.user_demo WHERE username=?")) {
count.setString(1, "jdbc-smoke");
try (ResultSet result = count.executeQuery()) {
if (!result.next() || result.getInt(1) != 1) throw new SQLException("canary 读取结果异常");
}
}
try (PreparedStatement delete = connection.prepareStatement(
"DELETE FROM app.user_demo WHERE username=?")) {
delete.setString(1, "jdbc-smoke");
if (delete.executeUpdate() != 1) throw new SQLException("canary 清理行数异常");
}
connection.commit();
System.out.println("kingbase runtime identity/write/read/delete ok");
} catch (Exception error) {
connection.rollback();
throw error;
}
}
}
}V8 基线设置 KINGBASE_JDBC_URL=jdbc:kingbase8://127.0.0.1:54321/app_tool_efficiency;V9 必须用该版本手册确认后的 jdbc:kingbase86、对应 jar 与 driver class。成功输出只证明 runtime 事务链,前面的 DDL 拒绝仍必须作为独立负例保留。
把同一张表恢复到隔离 database
备份与恢复也从容器内工具开始,避免只下载 Docker tar 的读者在宿主机遇到 sys_dump: command not found。先在容器内生成纯文本 dump,再复制到宿主机:
docker exec kingbase sys_dump -U app_tool_efficiency \
-f /tmp/app_tool_efficiency.sql app_tool_efficiency
docker cp kingbase:/tmp/app_tool_efficiency.sql ./app_tool_efficiency.sql恢复必须在临时库试跑,不要只生成文件。下面的临时库不得指向共享或生产实例:
docker cp ./app_tool_efficiency.sql kingbase:/tmp/app_tool_efficiency.sql
docker exec kingbase ksql -Ukingbase -d test \
-c "CREATE DATABASE app_tool_efficiency_restore OWNER app_tool_efficiency;"
docker exec kingbase ksql -Uapp_tool_efficiency \
-d app_tool_efficiency_restore -f /tmp/app_tool_efficiency.sql
docker exec kingbase ksql -Uapp_tool_efficiency \
-d app_tool_efficiency_restore \
-c "SELECT count(*) FROM app.user_demo;"恢复结果应能查到与源库一致的业务行数,不能只看目标库中“有一张表”。用同一摘要查询比较两端的总行数与贯穿全文的 demo 记录:
docker exec kingbase ksql -Uapp_tool_efficiency -d app_tool_efficiency -t -A \
-c "SELECT COUNT(*) || ':' || SUM(CASE WHEN username='demo' THEN 1 ELSE 0 END) FROM app.user_demo;" \
> /tmp/kingbase-source-summary.txt
docker exec kingbase ksql -Uapp_tool_efficiency -d app_tool_efficiency_restore -t -A \
-c "SELECT COUNT(*) || ':' || SUM(CASE WHEN username='demo' THEN 1 ELSE 0 END) FROM app.user_demo;" \
> /tmp/kingbase-target-summary.txt
cmp /tmp/kingbase-source-summary.txt /tmp/kingbase-target-summary.txt两端输出必须一致,且 demo 计数至少为 1;再用恢复库的应用身份写入并回滚一条 canary,证明目标实例可以承接应用事务。保留导出和恢复退出码、文件大小、对象数量、关键表摘要与 JDBC 自检结果;校验完成后先断开恢复库连接,再由管理账号删除恢复库和 runtime 用户,最后删除源库中的 app.user_demo、容器内 /tmp 文件和宿主机测试 dump。若恢复失败,保留临时库和日志用于定位,不要覆盖原 dump,也不要在原库反复重放。
把迁移、连接池与清理收进项目边界
项目里建议保留:
compose.yaml
.env.example
db/
kingbase/
init/
001_user.sql
002_schema.sql
migration/
verify/
verify.sql
dump/
docs/
dependency-setup.md
scripts/
kingbase-up.sh
kingbase-verify.sh
kingbase-export.sh
kingbase-reset-dev.sh迁移工具接入前先验证:
JDBC 驱动是否进入内部 Maven 仓库或依赖管理。目标 DB_MODE 下 Flyway / Liquibase DDL 是否可用。schema history 表落在哪个 schema。
类型、自增、序列、空串、大小写、分页、日期函数是否符合项目预期。Docker 精简镜像是否缺少客户端或驱动,是否需要额外工具镜像。
连接池还要显式配置连接超时、校验查询、最大池大小、最小空闲连接和连接最长生命周期。池大小不能按“线程越多越快”设置:每个数据库连接都会占用服务端会话、内存和事务资源,应用实例数乘以单实例池上限必须落在数据库连接预算内,并为迁移、排障和管理连接留余量。
Spring Boot 可以把敏感字段拆开,避免完整 URL 进入日志:
spring:
datasource:
url: jdbc:kingbase8://127.0.0.1:54321/app_tool_efficiency
username: ${KINGBASE_RUNTIME_USER}
password: ${KINGBASE_RUNTIME_PASSWORD}
driver-class-name: com.kingbase8.Driver
hikari:
connection-timeout: 3000
validation-timeout: 2000
maximum-pool-size: 10
minimum-idle: 2
max-lifetime: 1500000这些数值是开发示例,不是生产万能值。生产池大小由并发事务数、平均持有时间、数据库 max_connections 预算和实例数量共同决定;max-lifetime 还要短于网络设备或服务端强制断连时间,并通过连接重建指标验证。
用正反实验识别兼容差异
兼容模式的价值是降低迁移成本,不是抹平所有差异。先在目标 DB_MODE 中建立项目最小样例:主键生成、空字符串、大小写标识符、日期函数、分页、批量写入和锁等待。每个样例既要有预期成功路径,也要故意触发一个源库与目标库可能不同的结果。
用空字符串确认目标模式的真实语义
以空字符串为例,先执行:
CREATE TABLE app.compat_probe (
id INTEGER PRIMARY KEY,
value_text VARCHAR(20)
);
INSERT INTO app.compat_probe(id, value_text) VALUES (1, '');
SELECT id,
CASE WHEN value_text IS NULL THEN 'NULL' ELSE 'NOT_NULL' END AS null_state,
LENGTH(value_text) AS value_length
FROM app.compat_probe;不要预先把结果写死成某一种数据库的语义。把目标环境实际返回的 null_state 和 value_length 固化为迁移测试,再与源库结果比较。反向实验是让 ORM 写入空字符串后,以 IS NULL 和 = '' 分别查询;如果命中集合不同,业务中的必填校验、唯一约束和查询条件都要进入改造清单。
用锁等待确认事务和连接清理
再验证锁等待和回滚。连接 A 执行:
BEGIN;
UPDATE app.compat_probe SET value_text = 'held-by-a' WHERE id = 1;保持事务不提交,连接 B 对同一行执行更新。连接 B 应等待或在项目配置的超时后失败;此时记录错误码、等待时长和服务端会话状态。连接 A 执行 ROLLBACK 后,连接 B 应继续或按其超时语义退出。这个实验能暴露 ORM 超时、连接池借还、异常后的事务清理是否正确,比单纯跑通 CRUD 更接近真实故障。
从一次 JDBC 请求追踪提交与持久化
应用从连接池借到 JDBC Connection 后,驱动把 SQL 和绑定参数通过 KingbaseES 协议发给后端会话。服务端完成解析、绑定、优化和执行;读取要经过缓冲区与存储页,写入则在事务上下文中修改页面并产生恢复所需的日志。COMMIT 决定事务何时对其他会话可见以及持久化确认的边界,客户端收到成功并不等于备份、副本或异地容灾已经完成。
这条链路解释了几个常见现象:
SQL 很快但借连接很慢,瓶颈在应用连接池或连接预算,不在执行计划。更新阻塞而 CPU 不高,先查事务和锁等待,不要先扩容 CPU。容器重建后数据消失,问题在 volume 或数据目录;容器仍在并不能证明数据已持久化到正确位置。
dump 成功但恢复失败,备份介质存在不等于恢复链路可用。JDBC 类型映射异常,可能来自驱动与数据库补丁组合、兼容模式或 ORM 方言,而不是网络。
排障时先用 ksql 建立“服务端可连”的基线,再比较应用的 URL、账号、schema、驱动和连接池;这样能把网络/认证问题与框架问题分开。
从开发单库推导生产故障域
| 形态 | 解决的问题 | 主要代价 | 选择信号 |
|---|---|---|---|
| 单实例 | 开发、测试、低风险内部系统 | 单点、维护停机、容量受单机限制 | 数据可恢复且允许明确停机窗口 |
| 主备或读写分离 | 节点故障与部分读扩展 | 复制延迟、切换、旧主隔离、客户端路由 | RTO 要求低于人工重建时间 |
| RAC / 共享存储多写 | 更高可用与多节点并发 | 共享存储、集群协调、许可和运维复杂度 | 核心交易且平台具备成熟集群能力 |
| 分布式或分片形态 | 数据量和吞吐越过单机边界 | 分布式事务、路由、扩缩容和治理成本 | 单机容量经基线与优化后仍不足 |
| 托管或企业平台 | 降低安装、备份、监控负担 | 服务边界、成本、网络和厂商依赖 | 团队缺少专职数据库运维能力 |
高可用不等于数据安全。主备会复制误删除,RAC 不能替代备份,读写分离会引入延迟读,分片会把跨分片事务和全局唯一性推给架构。选型时把 RPO、RTO、峰值 TPS、数据量增长、连接数、最长事务、维护窗口、跨地域延迟、许可成本和团队值守能力放在同一张决策表里。
用容量、性能与成本预算约束形态
容量预算至少拆成数据、索引、事务日志、临时空间、备份和恢复空间。只按业务表行数估盘会遗漏索引膨胀、长事务保留、排序临时文件和升级时的双份空间。建立基线时记录:
SELECT current_database(), current_user;
SHOW server_encoding;
SHOW timezone;
SHOW max_connections;再从受支持的监控视图或企业平台采集数据库大小、表与索引增长、活动连接、等待事件、慢 SQL、日志增长和备份时长。具体系统视图会随版本变化,先在目标版本手册中确认字段,再写入监控模板。
成本也不只有 license:还包括计算、内存、数据盘、日志盘、备份介质、跨机房带宽、测试环境、驱动与补丁适配、迁移工具、恢复演练和 DBA 值守。容量评审要给出当前基线、增长率、扩容提前量和触发动作,而不是一个脱离负载的固定阈值。
查看日志:
docker logs kingbase --tail 120进入容器:
docker exec -it kingbase bash查看状态:
docker exec -it kingbase sys_ctl -D /home/kingbase/userdata/data status连接:
ksql -Ukingbase -d test -h 127.0.0.1 -p 54321查看对象:
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_schema NOT IN ('sys_catalog', 'information_schema')
ORDER BY table_schema, table_name;停止开发库:
docker compose down只有确认是本地开发数据,才允许删除 volume:
docker compose down -v四、问题处理
Docker tar 和 license 处理不清
现象:
docker pull kingbase 失败。镜像导入后没有 JDBC 驱动或客户端工具。试用授权到期或 license 持久化路径不清。
先停止向 CI 继续分发,保存容器实际 image ID、架构、入口、挂载和数据库版本:
docker inspect kingbase --format '{{.Image}} {{json .Config.Entrypoint}} {{json .Mounts}}'
docker image inspect "$KINGBASE_IMAGE" --format '{{.Id}} {{.Architecture}} {{json .RepoDigests}}'
docker exec kingbase ksql -Ukingbase -d test -c 'SELECT version();'从产品下载中心取得与目标 V8/V9、CPU/OS 对应的 tar、驱动和工具,记录试用授权 owner;不要把 Docker 精简镜像当成完整安装包。恢复标准是介质哈希、image ID、数据库补丁、JDBC 驱动和 license 责任能够互相对应,并从空 volume 重跑 USER_DEMO 与恢复链。
端口占用
排查:
lsof -i :54321
docker ps --format "table {{.Names}}\t{{.Ports}}"
docker port kingbase 54321本地可映射 154321:54321,并同步修改 JDBC、ksql、健康检查与恢复脚本。修复后用业务 runtime 身份查询 current_database()、current_user 和 version();端口通但落到另一套 KingbaseES 仍然是失败。
volume 权限不足
Docker 手册提醒挂载目录权限不正确会出现 Permission denied。先确认容器实际用户、宿主挂载和拒绝的路径:
docker image inspect "$KINGBASE_IMAGE" --format '{{json .Config.User}}'
docker inspect kingbase --format '{{json .Mounts}}'
docker logs kingbase --tail 160
namei -l /tmp/kingbase-data按镜像说明把目录 owner 调整为容器内数据库 UID/GID,并只赋予所需读写执行权限;不要用 chmod -R 777,也不要在不知道实际 UID 时机械套用 755。共享开发机的目录和 SELinux/AppArmor 标签由平台 owner 统一。恢复标准是同一 volume 经容器重建后保留原 cluster identity、数据和初始化模式,而不是悄悄初始化出一套新库。
初始化模式选错
现象:
DB_MODE=oracle 下 PostgreSQL 行为不符合预期。DB_MODE=pg 下 Oracle 空串兼容参数被误开。已有数据后想切模式。
先保存 SELECT version()、encoding/locale/timezone、初始化配置、schema DDL 和兼容用例结果,冻结业务写入。初始化前本应决定模式;已有库应在正确 DB_MODE 的隔离实例上重新建库、导入并对比空串、大小写、类型、序列、日期、分页与锁,不能在原数据目录切来切去。
恢复标准是兼容用例、runtime 权限、真实 JDBC 和 dump 恢复全部在新实例通过;只让某条源库 SQL 通过不能证明迁移完成。
JDBC URL 特殊字符
官方 JDBC 文档提醒 URL 中特殊字符会影响解析。先用同一 endpoint 和 runtime 身份执行 ksql,再保存脱敏 URL、V8/V9 driver class、jar 校验值、JDK 与最终依赖树;这样能区分认证/网络错误和驱动解析错误。
用 Properties 或框架独立字段传 username/password。需要拼 URL 时做 URL encode。不把真实密码放在 URL 里提交。
修复后必须由 KingbaseSmoke 完成身份、写入、读取和删除,同时独立 DDL 负例仍返回权限拒绝;只把密码改成简单字符来绕过 URL 解析不算修复。
不能进入仓库的内容:
KINGBASE_PASSWORD、KINGBASE_APP_PASSWORD、.env。真实 JDBC URL、内网 IP、共享库账号、license 文件。官方下载验证码、受控下载链接、内部镜像仓库凭证。
dump、SQL 导出、日志和客户数据。客户端连接配置、管理工具截图和生产连接历史。
.gitignore 建议:
.env
db/kingbase/dump/*.sql
db/kingbase/dump/*.dump
db/kingbase/logs/**
db/kingbase/license/**密码轮换要支持新旧凭证短暂并存或按实例滚动重启:先创建或更新目标账号,验证新 Secret 能建立连接,再滚动应用并观察旧凭证连接归零,最后回收旧凭证。直接改密码而不处理连接池,会让旧连接暂时可用、新建连接持续失败,形成难以判断的间歇故障。
共享 KingbaseES 开发库至少要有:
安装包 / Docker tar 来源、版本、试用授权、license owner。镜像 tag / digest 和内网仓库地址。DB_MODE、ENCODING、locale、timezone。
端口、database、schema、user 命名规范。runtime / migration / readonly 账号。JDBC 驱动版本和获取方式。
dump / restore 验证路径和清理周期。Oracle / PostgreSQL / MySQL 兼容模式验证清单。
每个环境还应有数据库 owner、应用 owner、license owner 和恢复演练 owner。季度或重要版本前复核补丁、安全公告、驱动组合、证书与凭证到期、备份恢复、容量趋势和闲置实例;项目下线时先冻结写入并确认数据保留要求,再回收账号、网络规则、Secret、GUI 连接、备份、license 分配和实例资源。
用升级、回退和准入门禁降低复发
升级和回退要成对设计
升级前在同 DB_MODE、encoding、locale 和 timezone 的隔离实例恢复一份脱敏数据,依次验证数据库补丁、JDBC 驱动、迁移工具和关键 SQL。数据库升级与驱动升级分两个变更窗口,能更快定位协议或类型映射回归。
回退不能只写“换回旧镜像”。一旦新版本修改了数据目录、系统目录或业务 schema,旧二进制未必能安全读取。变更单至少要说明:回退依赖原数据目录还是恢复前快照,业务写入何时冻结,迁移脚本是否可逆,旧驱动如何恢复,以及超过哪一时点只能前向修复。
| 深水区 | 现象 | 判断入口 | 配置 / 命令 / 取舍 |
|---|---|---|---|
| Docker tar 当镜像仓库 | docker pull 不存在 | 下载页、Docker 手册 | docker load 并记录来源 |
| 精简镜像误判 | 缺 JDBC / 工具 | 包类型说明 | 驱动单独下载 |
| 试用授权到期 | 容器突然不可用 | license owner、日志 | 提前告警并准备续期或下线 |
| 54321 冲突 | 本机多库混连 | lsof、JDBC URL | 宿主端口项目化 |
| system 滥用 | 应用拿管理账号连接 | 配置扫描、session | 应用独立用户 |
| DB_MODE 后改 | SQL 行为前后矛盾 | 初始化参数、迁移记录 | 初始化前定稿 |
| encoding 模糊 | 中文乱码、排序异常 | SHOW server_encoding | 显式设置并验证 |
| volume 权限 | 启动报 Permission denied | docker logs、宿主权限 | 目录权限和 owner 校准 |
| JDBC 特殊字符 | 密码含 & 后连接失败 | URL、Properties | 分离参数 |
| PG/Oracle 兼容混乱 | ORM SQL 不符合预期 | DB_MODE、dialect | 样例验证 |
| 导出未恢复 | 有 SQL 文件但恢复失败 | 临时库恢复 | 每次模板保留恢复 smoke |
| 连接预算失控 | 实例一扩容数据库就拒绝连接 | 池指标、活动会话、max_connections | 按应用实例数分配预算并保留管理余量 |
| 长事务拖累存储 | 日志与磁盘持续增长 | 事务年龄、锁等待、日志增长 | 事务超时、批次拆分、owner 告警 |
| 补丁与驱动错配 | 类型映射或连接行为升级后变化 | 数据库补丁、驱动版本、回归用例 | 分窗升级并保留回退组合 |
| 误连生产 | reset 脚本删错库 | URL、端口、账号 | 白名单和二次确认 |
KingbaseES 的风险不会停在 Docker tar 和端口。初始化模式决定语义,驱动组合决定应用行为,连接与事务决定并发边界,备份恢复决定数据底线,license、补丁和 owner 决定环境能否长期运行。只有这些状态都可追踪,“能连”才真正升级为可交付。
用可复核证据完成交付
交付环境前逐项确认:
是否核对 KingbaseES 包类型、Docker 参数、JDBC 行为和 initdb 参数。是否说明 Docker 镜像是 tar 导入,不写第三方镜像为推荐入口。是否记录 tar 包来源、版本、license owner、镜像 tag 和 digest。
是否写清 54321、DB_USER、DB_PASSWORD、DB_MODE、ENCODING。是否说明 Docker 精简镜像不等于完整安装介质。是否覆盖 ksql、JDBC driver、连接串和特殊字符处理。
是否提供建库、建用户、schema、建表、写入、读取和清理闭环。是否说明 Oracle / PG / MySQL 兼容模式初始化边界。是否提供导出恢复入口。
是否写清 license、dump、真实连接串和下载凭证的敏感信息风险。是否列出端口冲突、volume 权限、JDBC、encoding、DB_MODE 和误连生产的排查路径。是否根据 RPO、RTO、容量、并发、成本和团队能力选择单机、主备、RAC/RWC、分布式或托管形态。
是否完成最小权限的正向 DML 与反向 DDL 拒绝实验。是否验证连接池预算、事务回滚、锁等待超时和连接清理。是否为升级、回退、恢复演练、凭证轮换和项目下线指定 owner。
KingbaseES 不是“端口 54321 的 PostgreSQL 或 Oracle”。把 Docker tar、初始化模式、JDBC、license 和团队责任写实,它才能成为可交付的开发验证工具。
