上午接到一个数据分析需求,要从某个业务系统的Kingbase数据库里取几张大表做关联统计。我第一反应是打开DuckDB,打算用postgresql插件把库接进来,结果连了三次都是同样的报错:connection refused。折腾了半个多小时才意识到,问题不在DuckDB这边,而在Kingbase服务端的两个配置文件没有放行,而这两个文件偏偏只能用vi在Linux上改。由此把整条“在Linux上用vi修改duckdb postgresql插件访问Kingbase”的链路完整走了一遍,从原理到配置再到排错都摸了一遍。这篇文章就把整个过程拆开讲清楚,适合正在做跨库分析、需要在Linux上接Kingbase的工程师,也适合第一次接触DuckDB但想快速跑通的人。
1. 这个连接为什么成立:协议兼容性决定方案边界
1.1 标题里的“修改duckdb postgresql插件”到底改的是什么
很多人看到这个标题,第一反应是去找DuckDB插件目录下的某个配置,改几行参数,把插件从“连PostgreSQL”拧成“连Kingbase”。说实话,我第一次也有这个念头,但实际做下来并不是这么回事。
DuckDB官方生态里确实有一个postgres扩展,就是这个“postgresql插件”。它的本质是一个实现了PostgreSQL客户端协议的连接器,通过libpq把查询下推到远端的PostgreSQL系数据库去执行。平时我们写INSTALL postgres;和LOAD postgres;,只是把它安装并加载到当前DuckDB会话里,并不需要去改它的源码或配置文件。
真正需要在Linux上动用vi修改的,是Kingbase服务端的监听和认证配置。也就是postgresql.conf、pg_hba.conf这类文件。所以这篇文章的完整意思是:把DuckDB的postgres扩展加载好,再把Kingbase服务端配置调整到位,让两边能通过同一套PostgreSQL协议握手。
1.2 Kingbase为什么可以被当成PostgreSQL来连
Kingbase与PostgreSQL的关系,可以理解成在PostgreSQL内核之上做了大量扩展和适配的数据库产品。这意味着它最底层的通信协议、SQL语法解析、系统目录结构都和PostgreSQL高度一致。DuckDB的postgres扩展不认识“Kingbase商标”,但它认识PostgreSQL协议。只要Kingbase监听在网络端口上,DuckDB用postgres扩展去连接,两边就能对上话。
这里有一个关键点需要确认:目标Kingbase实例的兼容模式。大多数场景下,Kingbase会提供一个PostgreSQL兼容模式,也有Oracle兼容模式。如果初始化实例时选的是Oracle兼容模式,那么部分数据类型、大小写规则、系统函数的行为会和PostgreSQL略有差异。DuckDB的postgres扩展虽然是按PostgreSQL协议设计的,但只要查询语句能通过协议正常下发,绝大多数SQL还是可以执行。只是遇到Oracle风格的类型或函数时,需要做额外的类型转换或改用标准SQL写法。
1.3 版本选型和扩展安装方式
DuckDB的扩展和主程序版本是严格绑定的。版本对不上,LOAD postgres;时会直接报错提示version mismatch。最省事的做法是先安装DuckDB,然后进入命令行直接执行INSTALL postgres;,让它自动匹配当前版本并下载对应的扩展包。
如果你所在的环境是内网离线环境,不能联网下载扩展,那就需要手工准备与DuckDB版本严格匹配的扩展包,放到~/.duckdb/extensions/目录下,并确认扩展目录版本号的路径正确。这个坑我在实际项目中遇到过,下载扩展的机器和离线机器版本差了0.1.2,结果加载扩展时控制台只给了一行混乱的version mismatch,不仔细看根本猜不到是扩展版本问题。
2. 打开vi之前,先摸清Kingbase的配置家底
2.1 找到数据目录和配置文件
连接不通的第一反应是检查端口,但检查端口之前你得先知道Kingbase的配置在哪。Kingbase在Linux上的安装路径没有统一标准,常见的位置可能类似/opt/kingbase/data,也可能在用户目录下,具体看你机器当初是怎么装的。最稳妥的定位方法是查看进程。
用ps -ef | grep kingbase查看数据库服务进程,进程参数里一般会带数据目录路径-D。如果确认了目录还是不确定,就ls -la该目录,看到postgresql.conf和pg_hba.conf基本就没错了。这两个文件就是整条链路的咽喉。
2.2 需要关注的四个文件
真正需要动vi的地方主要集中在服务端,我把角色分工整理成了一张表,后面操作时盯着这张表就不会乱。
| 文件 | 作用 | 连接失败时的影响 |
|---|---|---|
| postgresql.conf | 监听地址、端口、连接数 | 只能本机连,外部连接直接被拒绝 |
| pg_hba.conf | 客户端认证规则 | 即使端口通了,认证不匹配也会登录失败 |
| kingbase.conf | 金仓特有的扩展参数 | 只在涉及专用特性时需要关注 |
| DuckDB端配置文件 | 几乎不需要手工改 | 用INSTALL和LOAD命令完成扩展管理 |
大部分报错场景都出在前两个文件。第三个文件在金仓实例里通常也是存在的,但常规DuckDB跨库查询基本碰不到它的设置项,了解到这个程度就够了。
2.3 vi求生手册:搜索、跳转、保存
既然标题明确是“用vi”,这里就把最实用的vi操作列一下,方便不熟vi的人也能顺利操作。
- 用
/listen_addresses搜索关键字,按n跳到下一个匹配项。 - 用
:set number显示行号,快速定位到指定行。 - 移动到行首用
0,行尾用$,上下左右用方向键即可。 - 按
i进入插入模式,改完按Esc退出到普通模式。 - 保存退出是
Shift+;后输入wq。如果文件只读,用wq!强制保存。 - 不想保存就直接按
Shift+;输入q!。
如果你修改的是一个需要root权限才能写的文件,直接sudo vi 文件路径进入编辑,不要在已经打开文件之后再后悔。
3. 用vi改服务端两个关键文件:放行监听和认证
3.1 修改postgresql.conf:放开监听地址
Kingbase默认监听地址有可能是localhost,端口默认不是PostgreSQL习惯的5432,而是54321。连接被拒绝往往先从这两个参数查起。
用vi打开postgresql.conf,搜索listen_addresses。常见的默认值有两种:一种是注释状态,另一种是localhost。要放行外部连接,需要把它改成:
listen_addresses = '*'然后把port一行的值确认为你的实际端口,比如54321。保存退出之前,顺手看一下max_connections,不用调大,但至少要留出足够的位置给DuckDB会话用。改完监听地址后,理论上服务会接受任意网卡的连接请求,但这时候去连接大概率还是失败,因为认证规则还没放行。
3.2 修改pg_hba.conf:添加客户端认证规则
pg_hba.conf是Kingbase的“门卫”。就算监听端口已经放开,门卫不放行,DuckDB一样连不进来。用vi打开这个文件,找到规则区,一般集中在文件尾部。常见做法是追加一行,大意是允许所有来源IP用某一种认证方式连接所有库。
host all all 0.0.0.0/0 scram-sha-256如果你只想放行特定的分析机IP,就把0.0.0.0/0替换成具体的IP段。认证方式这块要特别小心,需要和Kingbase服务端设置的password_encryption参数保持一致。多数新版本默认是scram-sha-256,但有些历史实例还是md5。不确定的话,先查看服务端当前已有的规则是用什么认证方式,照着已有的写法加新规则,成功率最高。
pg_hba.conf的匹配原则是第一行匹配生效。如果你加的新规则被前面的拒绝规则抢先匹配了,那加了多少行都没用。所以我一般会把新规则追加到文件末尾,同时确认前面没有更严格的host all all规则。
3.3 重启服务并确认端口处于监听状态
配置文件改完不会立即生效,需要重启服务。具体重启命令取决于你本机的安装方式,比如systemd管着的服务、或者安装目录下的管理脚本,用你实际安装时注册的服务名操作。重启之后不要急着去配DuckDB,先确认端口真的在监听:
ss -lntp | grep 54321如果这行输出为空,说明要么监听地址还没生效,要么端口写错了。如果看到监听地址是0.0.0.0:54321,说明服务端已经准备好接受外来连接了。到这一步,服务端的vi操作才算真正结束。
4. 在DuckDB里加载postgres扩展并完成ATTACH
4.1 INSTALL和LOAD的正确姿势
打开DuckDB命令行,先做两件事:
INSTALL postgres; LOAD postgres;INSTALL负责下载并安装扩展,LOAD负责把扩展加载进当前会话。扩展是一次性的全局安装,但每次打开新的DuckDB会话都需要重新LOAD。如果你觉得麻烦,可以把这行命令写进DuckDB的初始化脚本里,每次启动自动加载,但这里不多展开。
如果LOAD时报错,先检查DuckDB和扩展的版本是否匹配,再检查扩展文件所在目录是否可读。这两点排除了,基本就能顺利加载。
4.2 ATTACH连接串的写法与参数
扩展加载好之后,用ATTACH语句把Kingbase挂进来。推荐把连接串的TYPE显式写成postgres,避免DuckDB猜测类型。
ATTACH 'host=127.0.0.1 port=54321 dbname=your_db user=kingbase password=your_password' AS kdb (TYPE postgres);这里有一个细节值得强调:port必须写Kingbase的真实端口,而不是PostgreSQL的默认5432。密码如果有特殊字符,建议整个连接串都用单引号包起来,同时把特殊字符转义。连接成功之后,就可以用kdb这个别名引用远程库。
4.3 从SELECT到跨库JOIN的实际查询体验
连接成功之后,先试一条最小的查询,确认读取链路是通的:
SELECT * FROM kdb.public.some_table LIMIT 5;如果这条能跑,紧接着就可以做跨库JOIN了。比如DuckDB本地有一张维度表local_dim,Kingbase里有一张业务事实表kdb.public.fact_table,可以直接写:
SELECT l.dim_name, COUNT(f.id) FROM local_dim l LEFT JOIN kdb.public.fact_table f ON l.id = f.dim_id GROUP BY l.dim_name;DuckDB的postgres扩展会把远端表作为数据源拉取,并且在很多场景下会把过滤条件下推到Kingbase端执行,减少回传的数据量。从实测看,关联条件两边有索引时,跑千万级事实表关联本地小维度表,性能仍然可以用。但要注意,DuckDB和Kingbase之间的数据通路不是实时双写,DuckDB不会持续维护远程表状态,每次查询都是即时访问远端库。
5. 翻车现场复盘:端口、认证、类型映射与vi权限
5.1 坑:端口不是5432而是54321
第一次连接失败时,我盯着报错里的connection refused看了很久,差点以为是DuckDB的postgres扩展没装好。后来打开ss一看才发现,Kingbase服务监听的是54321,而我的连接串里写的是5432。这个错误太典型了,凡是从PostgreSQL转过来的用户,十有八九会掉进这个坑。
改端口时顺便检查一下防火墙,如果开了系统防火墙,还要确认防火墙规则也放行了对应端口,否则服务端监听没问题,外面照样连不进来。
5.2 坑:认证方式冲突导致password authentication failed
端口通了以后,我又遇到了password authentication failed for user。问题是服务端pg_hba.conf里已有规则用的是md5,而我新加的规则用了scram-sha-256,两边认证算法不一致。即使密码完全正确,认证环节也过不了。
解决方法是统一认证方式。先查pg_hba.conf里实际生效的规则,再检查postgresql.conf里的password_encryption参数,两边对齐。如果服务端用户密码还是旧算法生成的,改了认证算法之后可能需要在服务端重新设置一次用户密码才能生效。
5.3 坑:大小写规则与Oracle兼容模式的类型映射
Kingbase如果初始化成Oracle兼容模式,行为会和纯PostgreSQL模式有差异。一个典型现象是,未加引号的标识符可能被转换成大写存储。DuckDB和PostgreSQL默认都遵循未加引号小写化规则,查询时如果表名、字段名对不上,就会报relation does not exist或者column不存在。
解决办法是查表时引用明确的名称,必要时给表名加双引号。类型映射方面,Oracle兼容模式下的NUMBER可能在DuckDB里从integer变成decimal(38,0),VARCHAR2映射为varchar基本没问题,但遇到日期时间精度或布尔值转换时,建议用CAST显式转成目标类型,避免隐式转换带来的性能损耗和异常结果。
5.4 坑:vi编辑无权限与swap文件残留
修改服务端配置时经常遇到文件只有root能写,普通用户vi打开后只能只读浏览。遇到这种情况,先退出编辑器,用sudo vi 文件路径重新打开再改。效率高也不会改一半保存不了。
另一个烦人的问题是vim提示E325,发现swap文件。这通常意味着上次编辑没有正常退出,留下了.swp残留文件。处理前先确认没有另一个vi进程正在编辑同一个文件,如果没有,删除对应的swap文件再重新打开即可。不要一看到E325就直接删,万一真有另一个终端开着编辑,数据就白改了。
改配置文件之前养成先备份的习惯,把原文件复制成.bak再动手,比任何技能都可靠。翻车翻多了,这个习惯自然就有了。