从 MySQL 转向 KingbaseES 时,很多开发者都会遇到一个认知盲区:明明连上了数据库,表却建在了意想不到的位置;换个用户查询,结果竟然完全不同。这些问题根源在于 Database、Schema、User 三者概念被混淆。本文通过一个简单实验,帮你一次理清这三者的关系,避免迁移和日常开发中的低级错误。
为什么连上数据库不等于万事大吉?
在 MySQL 中,USE database_name 后,后续所有操作都默认在该数据库下进行,Database 直接充当了命名空间。但在 KingbaseES 中,连接目标、对象命名空间、登录用户是三件独立的事。
database:连接到哪个数据库
schema:表、视图、函数这些对象放在哪个命名空间
user/role:当前是谁在执行操作 这意味着:你连上某个数据库,并不代表你的表就一定建在该数据库下,还需要考虑当前 Schema 和用户身份。核心区别:MySQL 的 Database ≈ KingbaseES 的 Schema;而 KingbaseES 的 Database 是更外层的连接边界,Schema 才是真正的对象容器。搞混这一点,后续会遇到“表存在但查不到”“同名表数据不一致”等奇怪问题。
⚙️ 实验准备:一个用户、一个数据库、一张同名表
为了最小化干扰因素,我们只创建一个普通用户 app_user 和一个数据库 app_db,然后故意复用表名 t1,让它分别出现在不同 Schema 下。这样,你能清晰看到:同一个 Database、同一个 User、同一个表名,只因为 Schema 不同,查询结果就会变化。
首先,用普通用户连接数据库:
ksql -h 127.0.0.1 -p 54321 -U app_user -d app_db 进入后查询三个关键值:select current_database(), current_user, current_schema();
show search_path; 返回:当前数据库是 app_db,当前用户是 app_user,当前 Schema 是 public。"$user", public
这里已经能看到概念被拆开了:连接命令中的数据库名只决定当前 Database;登录用户名决定当前 User;而真正不写前缀建表时,表会落到当前 Schema 下。"$user", public 的意思是:先找与当前用户同名的 Schema,再找 public。当前环境没有 app_user 这个 Schema,所以默认 Schema 是 public。
不写 Schema,表会落到哪里?
直接建一张表:
drop table if exists t_schema_demo;
create table t_schema_demo(id int, name varchar(50));
insert into t_schema_demo values (1, 'from default schema'); 然后用 \dt 和 \d 查看:\dt
\d t_schema_demo 同时查询系统视图:select schemaname, tablename, tableowner
from sys_tables
where tablename = 't_schema_demo'; 结果很直接:表落在 public 下,Owner 是 app_user。
这就是 search_path 生效后的结果。⚠️ 换成 MySQL 习惯时,最容易想当然:已经连上 %%PROTECTED_INLINE_CODE_82%,所以表就在 %%PROTECTED_INLINE_CODE_83% 里。更准确的说法是:当前连接在 %%PROTECTED_INLINE_CODE_84%,表对象属于 %%PROTECTED_INLINE_CODE_85% 里的 %%PROTECTED_INLINE_CODE_86% Schema。换句话说,Database 是连接边界,Schema 才是命名空间。
写 DDL 时最好先确认这三件事。只知道“当前连的是哪个库”还不够,至少还要知道默认 Schema 是什么。否则清理对象时,可能会发现同一个库里散着多个 Schema,表名也不一定唯一。
️ 同一个 Database 里可以有多个 Schema
创建一个新的 Schema:
create schema app_schema authorization app_user; 再查询当前数据库里的 Schema:select schema_name
from information_schema.schemata
where schema_name in ('public', 'app_schema'); 结果中能看到 public 和 app_schema。
app_schema 不是新数据库,它只是 %%PROTECTED_INLINE_CODE_100% 里的一个命名空间。authorization app_user 表示这个 Schema 归 %%PROTECTED_INLINE_CODE_102% 所有。在真实项目中,Schema 常用来做模块隔离:比如一个库里放业务表、报表表、中间表,或者迁移时先把旧系统对象放到单独 Schema。这样不需要每个模块都拆成一个独立数据库,也能避免对象名冲突。
现在显式把表建到 app_schema 下:
create table app_schema.t_schema_demo(
id int,
name varchar(50)
);
insert into app_schema.t_schema_demo values (2, 'from app_schema'); 再查 information_schema.tables:select table_schema, table_name
from information_schema.tables
where table_name = 't_schema_demo'
order by table_schema; 结果出现两行:app_schema | t_schema_demo
public | t_schema_demo
这就是 Schema 的作用。同一个 Database 里,不同 Schema 可以存在同名表。完整对象名分别是:
public.t_schema_demo
app_schema.t_schema_demo 这两个名字完整写出来以后,就不冲突了。排查问题时,如果只看到一个裸表名,不要马上以为它指向唯一对象,先查归属,再看搜索路径。️ search_path 如何影响查询结果?
加上 Schema 前缀后,查询目标很明确:
select * from public.t_schema_demo;
select * from app_schema.t_schema_demo; 前一张表返回:1 | from default schema 后一张表返回:2 | from app_schema
但如果不带前缀,查询结果取决于当前 search_path。先把 app_schema 放到前面:
set search_path to app_schema, public;
show search_path;
select * from t_schema_demo; 返回的是 app_schema.t_schema_demo 里的数据:2 | from app_schema 再把 public 放到前面:set search_path to public, app_schema;
show search_path;
select * from t_schema_demo; 返回变成 public.t_schema_demo 里的数据:1 | from default schema
✅ 这一步能解释很多看起来很怪的问题:表存在,查询也没报错,但结果不是预期数据,原因可能不是 SQL 写错,而是未加前缀的表名被解析到了另一个 Schema。排查“查错表”时,search_path 应该和 current_database、current_user 一起看。
换用户不等于换数据库
退出 app_user 后,换 system 连接同一个数据库:
ksql -h 127.0.0.1 -p 54321 -U system -d app_db 查当前位置:select current_database(), current_user, current_schema(); 结果变成:app_db | system | public 继续查 t_schema_demo 的归属:select table_schema, table_name
from information_schema.tables
where table_name = 't_schema_demo'
order by table_schema; 仍然能看到:app_schema | t_schema_demo
public | t_schema_demo
换用户不等于换数据库,也不等于把对象搬到别的 Schema。system 只是换了当前执行 SQL 的身份,连接目标仍然是 app_db,对象仍然在 public 和 app_schema 下面。这也说明:用户不是数据库,数据库也不是用户。用户影响权限和默认 Schema 解析,但不会因为换用户就把同一数据库里的对象改名或搬走。
[AFFILIATE_SLOT_1]
迁移实践:从 MySQL 到 KingbaseES 的关键转换
迁移时,不能机械地将 USE db_name 改成 \c db_name。更常见的处理是:连接到目标 Database,然后把对象放进指定 Schema,再用 schema.table 访问。具体设计取决于项目是否需要多 Schema、是否要保留原库名、是否要隔离临时迁移对象。
最佳实践建议:
- ✅ 迁移脚本、初始化脚本、定时任务、跨 Schema 查询:最好显式写出 Schema 全名,避免因 search_path 不同导致歧义。
- ✅ 多人共用测试库时:写清 Schema 能避免无意义的来回确认,方便清理和复盘。
- ✅ 日常业务 SQL:如果项目固定了默认 Schema,可以不写前缀,但排查问题时务必检查 search_path。
在微服务或后端架构中,多个服务共享同一个 Database 但使用不同 Schema 是一种常见模式,既能复用连接池,又能实现逻辑隔离。理解这三者的关系,是设计稳健数据库架构的基础。
总结
Database 是连接边界,Schema 是命名空间,User 是执行身份。三者相互独立,又共同决定对象的创建、查找和访问。从 MySQL 迁移到 KingbaseES 时,务必摒弃“一个库等于一个命名空间”的惯性思维,建立“Database → Schema → 对象”的三层认知。搞清这一点,能避免 90% 的“查错表”“建错位”问题,让迁移和日常开发更加顺畅。
[AFFILIATE_SLOT_2]
浙公网安备 33010602011771号