OpenTenBase 是腾讯开源的企业级分布式数据库,但官方支持列表里并没有 Ubuntu 22.04。
这篇教程记录了在 Ubuntu 22.04 上从零开始源码编译、部署并验收一套单机分布式集群
(1 个 GTM + 1 个 CN + 2 个 DN)的完整过程,重点是解决官方文档没有覆盖到的
依赖与兼容性问题。全部命令均在实测环境中验证通过。本文为第 3 版(2026-09-25 修订):新增「写在前面」阅读须知与「日常使用」两章,
为全部命令补充了逐行注释,修正了依赖清单(uuid-dev 与 libossp-uuid-dev 须同时安装),
并全面扩充了排错表与实测踩坑记录。
目录
- 写在前面(阅读须知)
- 引言
- 环境准备
- 获取源码
- 编译前的两处源码修改(核心)
- 编译与安装
- 配置集群
- 部署与启动集群
- 数据库初始化与验收
- 日常使用:重启后启动集群与基础 SQL 操作
- 常见问题与排错
- 附录
写在前面(阅读须知)
本章说明本教程使用的记号约定与几个常用操作,后续章节不再重复。请按章节顺序依次执行;每一步均提供验证方法,验证通过后再进行下一步。
0.1 终端的基本操作
终端(Terminal)是执行命令行操作的窗口,本教程所有命令均在终端中执行。在 Ubuntu 桌面按 Ctrl+Alt+T 即可打开一个新终端。
两组容易混淆的快捷键:在终端中,Ctrl+Shift+C 为复制、Ctrl+Shift+V 为粘贴(并非 Windows 习惯的 Ctrl+C / Ctrl+V);单独按 Ctrl+C 的作用是终止当前正在运行的命令,后文排错时会用到。
0.2 提示符与输入约定
命令行最左侧会显示提示符,用于标识当前用户与环境。提示符本身不是命令的一部分,复制命令时不要将其带上。本教程会出现的提示符见表 0-1。
表 0-1 本教程中出现的提示符
| 看到的字符 | 含义 |
|---|---|
| lzh@ubuntu:~$ | 你自己的普通用户(本机装系统时的用户,本文以 lzh 为例) |
| root@ubuntu:~# | root 超级用户提示符(本教程不直接使用 root) |
| opentenbase@ubuntu:~$ | 第二章创建的 opentenbase 用户 |
| pgxc_ctl> | 第七章启动 pgxc_ctl 工具后的交互提示符 |
| postgres=# | 第八章进入 psql 数据库命令行后的提示符 |
| \(行尾) | 续行符,表示这条命令没写完,下一行接着写 |
输入约定:以 # 开头的行是注释,解释下一行或同一行命令的含义,不需要输入到终端;以 -- 开头的是 SQL 注释,同样不用输入。带续行符 \ 的命令请整块复制,包括反斜杠。
0.3 开始前的准备
确认系统版本。打开终端执行 lsb_release -a,Distributor 一行应显示 Ubuntu,Release 应为 22.04。其他版本不保证本文的补丁步骤适用。
给虚拟机拍快照。本教程涉及系统级配置修改,建议在 VMware 菜单中先执行「虚拟机 → 快照 → 拍摄快照」。后续任一步骤出现异常时,可通过恢复快照快速回到初始状态。
检查硬件。执行下面两条命令,对照第二章 2.1 节的最低要求。
# 查看内存:Mem 行的 total 应在 8G 左右,低于 4G 编译会非常吃力
free -h
# 查看磁盘:Filesystem 行里挂在 / 的分区 Avail 一列应预留 5G 以上
df -h
0.4 常用应急操作
- 终止当前命令:命令长时间未结束时,按 Ctrl+C 终止并回到提示符,不会造成任何破坏。
- 退出 vim:编辑过程中按键混乱时,先按 Esc 键,再依次输入
:q!回车,不保存退出,文件保持原样。第六章编辑配置文件时会给出 vim 的具体按键步骤。 - 退出交互界面:普通命令行输入
exit;pgxc_ctl 管理界面输入exit;psql 数据库交互界面输入\q。后文用到时会再次说明。
一、引言
1.1 OpenTenBase 是什么
OpenTenBase 是腾讯开源的企业级分布式数据库管理系统,由 OpenAtom 开放原子基金会孵化,前身是 TDSQL 社区版与 Postgres-XL 项目。它采用无共享(share-nothing)架构,集群由三类节点组成:
- GTM(全局事务管理器):负责分布式事务号与全局一致性;
- CN(协调节点 Coordinator):只存元数据,接收 SQL 后拆分下发;
- DN(数据节点 DataNode):存储分片数据。
用户统一连接 CN,对上层应用透明。内核基于 PostgreSQL 10,兼容大部分 PG 语法与生态工具。本教程在 Ubuntu 22.04 上从零开始源码编译、部署并验收一套单机分布式集群(1 个 GTM + 1 个 CN + 2 个 DN),重点是解决官方文档没有覆盖到的依赖与兼容性问题。全部命令均在下述实测环境中验证通过。
1.2 实测环境
表 1-1 实测环境
| 项目 | 配置 |
|---|---|
| 操作系统 | Ubuntu 22.04.5 LTS(jammy) |
| 内核 | 6.8.0-138-generic |
| 虚拟化平台 | VMware Workstation(Windows 宿主机) |
| CPU / 内存 | 4 核 / 8 GB(可用约 7.7 GB) |
| 磁盘 | 数据盘挂载在 /data,建议预留 20 GB 以上 |
| 软件版本 | OpenTenBase master 分支(内核号 V5.21,安装目录名 opentenbase_bin_v5.0) |
| 集群拓扑 | 单机:GTM :20001 + CN cn001 :30004 + DN dn001 :40004 + DN dn002 :40007 |
1.3 为什么需要这篇教程
官方 README 列出的支持系统是 TencentOS 2/3、OpenCloudOS 8.x、CentOS 7/8 和 Ubuntu 18.04,Ubuntu 22.04 并不在列。直接照官方步骤在 Ubuntu 22.04 上编译,会先后碰到 configure 硬编码路径、C 语言 bool 类型冲突等多个报错,本教程逐一给出原因分析与解决方案。
二、环境准备
2.1 硬件要求
官方要求最低 8 GB 内存。实测 make -sj3 编译时峰值占用约 4 GB,8 GB 内存的虚拟机足够;若只有 4 GB,请把编译并发降到 -sj2 并适当加大 swap。磁盘方面,源码约 500 MB、编译中间产物约 2 GB、安装产物约 200 MB、四个数据实例初始化后约 1 GB,共预留 5 GB 以上,生产环境建议 20 GB 以上。用 0.3 节的 free -h 和 df -h 自查。
2.2 安装系统依赖
在 Ubuntu 桌面的终端里,以你自己的用户(如 lzh)执行以下命令。sudo 会要求输入当前用户(lzh)的密码;输入过程中屏幕不显示任何字符,属正常安全机制,输入完成后直接回车。
# 更新 APT 软件包索引,确保能获取到最新软件包信息
sudo apt update
# 安装编译工具、常用开发库、SSH 工具和中文语言包
# -y:自动确认安装,不再逐个询问
# \ :Shell 续行符,表示下一行仍是同一条命令,整块复制即可
sudo apt install -y git gcc g++ make cmake bison flex \
libreadline-dev zlib1g-dev libssl-dev libxml2-dev \
libossp-uuid-dev uuid-dev libzstd-dev liblz4-dev libicu-dev \
libcurl4-openssl-dev libssh2-1-dev sshpass \
openssh-server language-pack-zh-hans
上面前半部分与官方 README 一致,但有几个包是官方清单没写、实际必需的,见表 2-1。其中 uuid-dev 与 libossp-uuid-dev 都要装:前者提供 uuid 库本身,后者提供 configure 检测的 OSSP UUID 接口,缺任一个都会在 configure 阶段报库缺失。如果还需要在虚拟机里截图做验收记录,可以额外安装 scrot。
表 2-1 关键依赖说明(★ 为官方清单未列出但实际必需)
| 软件包 | 用途 | 备注 |
|---|---|---|
| libzstd-dev ★ | 压缩库,configure 强制检测 | 官方清单未列出,缺少则 configure 报错 |
| liblz4-dev ★ | 压缩库,configure 强制检测 | 同上 |
| g++ ★ | C++ 编译器 | 编译 contrib 阶段需要,缺少则后半段 make 报错 |
| libicu-dev ★ | Unicode 支持 | 部分模块依赖 |
| libcurl4-openssl-dev ★ | HTTP 客户端库 | 部分模块依赖 |
| libossp-uuid-dev ★ | UUID 支持(OSSP 接口) | 配合 –with-ossp-uuid |
| uuid-dev ★ | UUID 库开发文件 | 与 libossp-uuid-dev 同时安装,缺一则 configure 报库缺失 |
| libreadline-dev | psql 交互命令行 | 官方清单已含 |
| libssl-dev | OpenSSL 加密支持 | Ubuntu 22.04 为 OpenSSL 3.0,引出第四章的 bool 补丁 |
| openssh-server | SSH 服务 | pgxc_ctl 高度依赖 SSH |
2.3 创建 opentenbase 用户
OpenTenBase 不允许用 root 运行,需要专用的运行用户。本教程约定:家目录与所有数据都放在 /data/opentenbase。
# 创建 /data 目录
# -p:递归创建父目录;如果目录已存在也不会报错
sudo mkdir -p /data
# 创建 opentenbase 用户
# -d /data/opentenbase:指定该用户的家目录为 /data/opentenbase
# -s /bin/bash:指定该用户登录后使用的 Shell 为 bash
# -m:如果家目录不存在,则自动创建
sudo useradd -d /data/opentenbase -s /bin/bash -m opentenbase
# 为 opentenbase 用户设置密码
# 会交互式提示输入两次新密码;同样输入时不回显
# 本教程以 opentenbase 为例,请记牢这个密码
sudo passwd opentenbase
# 将 opentenbase 用户追加到 sudo 组
# -aG:追加到附加组,不会覆盖用户原有的其他附加组
sudo usermod -aG sudo opentenbase
# 递归修改 /data/opentenbase 的属主、属组为 opentenbase
# -R:递归处理子目录和文件
sudo chown -R opentenbase:opentenbase /data/opentenbase
说明:后文多处出现
su - opentenbase:此时输入的是 opentenbase 的密码,而非 lzh 的。若报「认证失败」,多为密码输入有误;忘记密码时可用sudo passwd opentenbase重设。
2.4 配置 SSH 免密登录
pgxc_ctl 部署工具把每个节点都当作远程主机,即使全部节点都在本机 127.0.0.1,它也会通过 SSH 下发命令。因此 opentenbase 用户必须能免密 SSH 到自己,否则 deploy all 会反复要求输入密码甚至卡死。
# 切换到 opentenbase 用户(输入 2.3 节设置的密码)
# - 表示以登录方式切换,会加载该用户的环境变量和家目录配置
su - opentenbase
# 生成 SSH 密钥对
# -t rsa:指定密钥类型为 RSA
# -N "":设置私钥密码为空,便于实现免密登录
# -f ~/.ssh/id_rsa:指定私钥文件路径,公钥为 ~/.ssh/id_rsa.pub
ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa
# 将刚生成的公钥追加到授权密钥文件中
# >> 表示追加写入,避免覆盖已有公钥
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
# 修改 authorized_keys 文件权限为 600
# SSH 对权限要求严格,权限过大可能导致免密登录失效
chmod 600 ~/.ssh/authorized_keys
# 测试本机 SSH 免密登录:登录后执行 date 并返回结果
# 首次连接会问 Are you sure you want to continue connecting (yes/no)?
# 手动输入 yes 回车(不能只按回车)
ssh 127.0.0.1 date
# 退出 opentenbase 用户,回到原来的用户会话
exit
验证标准:执行
ssh 127.0.0.1 date后不再询问密码、直接打印出当前日期时间,即为成功。若仍要求输入密码,检查chmod 600这一步是否执行过。
注意(实测踩坑):若跳过 ssh-keygen 直接执行 cat 追加公钥,会报
-bash: /data/opentenbase/.ssh/authorized_keys: No such file or directory——报错出自 bash 而非 cat,说明 ~/.ssh 目录本身尚不存在(该目录由 ssh-keygen 自动创建),authorized_keys 自然无从创建,ssh 127.0.0.1 date也仍会要求密码。解决:补执行 ssh-keygen 生成密钥对,再依次重做 cat 追加与 chmod 600,直至验证通过。
三、获取源码
3.1 方式一:git clone(首选)
# 切换到 opentenbase 用户
su - opentenbase
# 进入 opentenbase 用户的家目录,源码将下载到这里
cd /data/opentenbase
# 从 GitHub 克隆 OpenTenBase 源码仓库
# 克隆完成后会在当前目录下生成 OpenTenBase 文件夹
git clone https://github.com/OpenTenBase/OpenTenBase.git
3.2 方式二:网络不顺时的替代方案
GitHub 直连不稳定时,git clone 常见两类报错:GnuTLS recv error (-110) 和 Connection refused。以下两个方案均实测可用,优先用方案 A。
# ============================================================
# 方案 A:通过 codeload 直接下载源码压缩包(约 36 MB,不走 git 协议)
# ============================================================
# 下载 master 分支源码压缩包
# -L:跟随 HTTP 重定向(codeload 地址通常会重定向)
# -o:将下载内容保存为指定文件名
curl -L -o OpenTenBase-master.tar.gz \
https://codeload.github.com/OpenTenBase/OpenTenBase/tar.gz/refs/heads/master
# 解压下载的 tar.gz 压缩包
# z:使用 gzip 解压;x:解压归档;f:指定要解压的文件
tar zxf OpenTenBase-master.tar.gz
# 解压生成的目录名是 OpenTenBase-master,必须改名为 OpenTenBase,
# 否则第四章的 cd /data/opentenbase/OpenTenBase 会失败:
mv OpenTenBase-master OpenTenBase
# ============================================================
# 方案 B:从 gitcode 平台镜像仓库克隆(国内网络更快)
# ============================================================
# git clone 会在当前目录下直接创建 OpenTenBase 目录,无需改名
git clone https://gitcode.com/OpenTenBase/OpenTenBase.git
目录名必须统一:两种方式获取的代码完全一致,但目录名必须统一为 /data/opentenbase/OpenTenBase(方案 A 下载的目录名为 OpenTenBase-master,必须执行 mv 改名)。无论采用哪种方式,均应执行以下命令验证,能列出 src、contrib 等目录方为通过:
# 验证源码目录就位:应输出 contrib src configure 等内容
ls /data/opentenbase/OpenTenBase
四、编译前的两处源码修改(核心)
这一章是整个安装过程中唯一需要动脑子的地方,也是本教程与官方文档的主要差异。两处修改都源于同一个背景:腾讯内部的编译环境(BINARYLIBS 机制)与我们本地的 Ubuntu 22.04 不同,但开源出的配置脚本没有完全适配。
4.1 问题一:configure 硬编码了 libzstd / liblz4 的绝对路径
现象:即使已经安装了 libzstd-dev,configure 仍然报错:
checking for zstd ... configure: error: zstd library not found
原因:用 grep 检查 configure 脚本可以发现,它没有使用系统动态库,而是硬编码了腾讯内部 BINARYLIBS 环境的静态库绝对路径(共 4 处,行号在 12802、12837、12852、12887 附近):
# 在 configure 脚本中搜索硬编码的 libzstd.a 和 liblz4.a 路径
# -n:显示匹配内容所在的行号
# "libzstd.a\|liblz4.a":基本正则表达式,\| 表示「或」
grep -n "libzstd.a\|liblz4.a" configure
# 输出说明(Ubuntu 上这两个文件通常并不存在):
# 12802: LIBS="/usr/local/lib/libzstd.a $LIBS"
# 12837: LIBS="/usr/local/lib/liblz4.a $LIBS"
解决:把硬编码路径改成链接器参数,让它去链接 apt 安装的系统库(依赖 2.2 节安装的 libzstd-dev 和 liblz4-dev):
# 进入 OpenTenBase 源码目录
cd /data/opentenbase/OpenTenBase
# 使用 sed 原地修改 configure 脚本:
# -i:直接修改文件内容,不输出到终端
# s|旧内容|新内容|g:替换语法,用 | 作分隔符避免与路径中的 / 冲突;g 表示全局替换
# 将 /usr/local/lib/libzstd.a 替换为 -lzstd(链接系统 libzstd 库)
# 将 /usr/local/lib/liblz4.a 替换为 -llz4 (链接系统 liblz4 库)
sed -i "s|/usr/local/lib/libzstd.a|-lzstd|g; s|/usr/local/lib/liblz4.a|-llz4|g" configure
# 验证替换结果:应看到 -lzstd / -llz4 出现在原来硬编码路径的位置
# head:只显示前 10 行,避免输出过多
grep -n "lzstd\|llz4" configure | head
4.2 问题二:char bool 与 OpenSSL 3.0 的 stdbool.h 冲突
现象:configure 顺利通过后,make 在编译后端源文件时大量报错:
In file included from src/backend/access/common/heaptuple.c:26:
src/include/access/htup_details.h:706:15:
error: conflicting types for 'getmissingattr';
have 'Datum(const TupleDescData *, int, bool *)'
{aka 'long unsigned int(const struct TupleDescData *, int, _Bool *)'}
note: previous declaration of 'getmissingattr' was here (tuptable.h, bool * -> char *)
原因:内核头文件 src/include/c.h 在 C 语言下用 typedef char bool; 自定义了 bool 类型(PostgreSQL 10 时代的写法)。而 Ubuntu 22.04 的 OpenSSL 是 3.0 版本,开启 –with-openssl 编译时,它的头文件会间接把标准头文件 <stdbool.h> 拉进同一个编译单元,其中 _Bool 是编译器内建类型,与 char 定义冲突,同一函数就出现了两个不同的原型。
解决:在 c.h 的 bool 定义之前提前引入标准 <stdbool.h>,让整个工程统一使用标准 _Bool。为避免手改出错,直接用下面的命令自动修改(先备份、后验证):
# 进入 OpenTenBase 源码目录
cd /data/opentenbase/OpenTenBase
# 备份原始 c.h,便于出错时恢复
cp src/include/c.h src/include/c.h.bak
# 用 Python 自动修改 src/include/c.h
# <<'EOF' 是 heredoc 写法:把两个 EOF 之间的内容原样传给 python3 执行
# 引号中的单引号表示不做 Shell 变量替换
python3 - <<'EOF'
import io
# 目标文件路径
p = "src/include/c.h"
# 读取整个文件
s = io.open(p, encoding="utf-8").read()
# 原始代码片段:bool 定义前没有引入 stdbool.h
old = "#ifndef bool\ntypedef char bool;"
# 替换后的片段:先引入标准 stdbool.h,再保留原有逻辑
new = "#include <stdbool.h>\n\n#ifndef bool\ntypedef char bool;"
# 断言原片段存在,避免匹配不到时静默写入无变化的内容
assert old in s, "pattern not found"
# 只替换第一处(count=1)并写回
io.open(p, "w", encoding="utf-8").write(s.replace(old, new, 1))
# 输出成功提示
print("c.h patched")
EOF
# 验证:应输出包含 #include <stdbool.h> 的那一行及其行号
grep -n "stdbool" src/include/c.h
# 再看一眼上下文,确认插入位置在 #ifndef bool 之前
grep -n -A 6 -B 2 "stdbool" src/include/c.h
重要:如果中途重新解压了源码包,两处修改都会被覆盖,必须重新执行本章的两个补丁步骤再编译。
4.3 其他常见报错速查
表 4-1 依赖类报错速查表
| 报错信息 | 原因 | 解决 |
|---|---|---|
| g++: No such file or directory(编译 contrib 阶段) | 未安装 C++ 编译器 | sudo apt install g++ |
| configure: error: ICU library not found | 缺 libicu-dev | sudo apt install libicu-dev |
| configure: error: libraries … curl 或 uuid 相关 | 缺对应 dev 包 | sudo apt install libcurl4-openssl-dev uuid-dev |
| Could not get lock /var/lib/dpkg/lock-frontend | 后台 unattended-upgrades 正在占用 apt 锁 | 等待其结束(ps -p <PID> 确认进程退出)后再装;不要强删锁文件 |
五、编译与安装
5.1 configure 参数说明
表 5-1 configure 参数说明
| 参数 | 作用 |
|---|---|
| –prefix=…/opentenbase_bin_v5.0 | 安装目录。目录名必须叫 opentenbase_bin_v5.0,集群工具按此约定寻址 |
| –enable-user-switch | 允许以普通用户(非 root)启动实例 |
| –with-libxml | 启用 XML 类型与函数 |
| –disable-license | 关闭腾讯内部 license 校验,社区版必选 |
| –with-openssl | 启用加密连接(会触发 4.2 节的补丁需求) |
| –with-ossp-uuid | 启用 UUID 生成函数 |
| CFLAGS=”-g” | 保留调试符号,便于出错时定位 |
5.2 一次性干净构建
重要经验:务必从全新解压的源码一次性完成 configure 和 make。多次反复 configure 会把不同时刻生成的二进制混在一起,埋下 5.4 节的版本不一致问题(笔者实测两个二进制的编译时间戳相差了 12 分钟,导致 initdb 拒绝启动 postgres)。如果之前编译失败过,先删掉源码目录和安装目录,重新解压、重打第四章两个补丁,再执行:
# 切换到 opentenbase 用户
su - opentenbase
# 设置源码目录环境变量,指向 OpenTenBase 源码根目录
export SOURCECODE_PATH=/data/opentenbase/OpenTenBase
# 设置安装目录环境变量,后续编译安装都放在这个目录下
export INSTALL_PATH=/data/opentenbase/install
# 确保安装目录存在,避免后续安装因目录缺失而失败
mkdir -p $INSTALL_PATH
# 进入源码目录
cd $SOURCECODE_PATH
# 删除旧的安装目录,确保本次是干净安装
# -r:递归删除;-f:强制删除不提示
rm -rf $INSTALL_PATH/opentenbase_bin_v5.0
# 赋予 configure 脚本可执行权限
chmod +x configure
# 运行 configure 进行编译配置
# --prefix:安装到 $INSTALL_PATH/opentenbase_bin_v5.0
# 其余参数含义见表 5-1
./configure --prefix=$INSTALL_PATH/opentenbase_bin_v5.0 \
--enable-user-switch --with-libxml --disable-license \
--with-openssl --with-ossp-uuid CFLAGS="-g"
# 编译源码(约 20~40 分钟)
# -s:静默模式;-j3:并行 3 个任务,8 GB 内存建议 3,充足可到 4
make -sj3
# 将编译好的文件安装到 --prefix 指定的目录
make install
环境变量只在当前终端有效:export 设置的 SOURCECODE_PATH 和 INSTALL_PATH 只在当前这个终端窗口里有效,新开一个终端或断开重连后就没了,需要重新执行上面两行 export。如果后面哪一步报「变量不存在」或路径为空,先检查是不是换了终端。
5.3 编译 contrib 工具(pgxc_ctl 在这里)
主体 make install 不会安装集群管理工具 pgxc_ctl,它在 contrib 目录里,需要单独编译(这一步会用到 g++):
# 回到源码根目录($SOURCECODE_PATH 是 5.2 节设置的环境变量)
cd $SOURCECODE_PATH
# 赋予 contrib/pgxc_ctl/make_signature 脚本可执行权限
# 不赋权可能导致后续步骤失败
chmod +x contrib/pgxc_ctl/make_signature
# 进入 contrib 目录(包含附加模块和扩展工具)
cd contrib
# 编译 contrib 下所有模块
make -sj3
# 安装到 configure 时指定的 --prefix 目录下
make install
5.4 版本一致性验证(必做)
安装完成后验证 initdb 与 postgres 的版本字符串必须完全一致,尤其是末尾的编译时间戳:
# 查看 initdb 的版本字符串,重点看末尾的编译时间戳
$INSTALL_PATH/opentenbase_bin_v5.0/bin/initdb --version
# 查看 postgres 的版本字符串,必须与上面完全一致
$INSTALL_PATH/opentenbase_bin_v5.0/bin/postgres --version
# 两行输出应类似(时间戳一致):
# initdb (PostgreSQL) 10.0 @ OpenTenBase_v5.0 OpenTenBase V5.21 2026-09-23 21:46:29
# postgres (PostgreSQL) 10.0 @ OpenTenBase_v5.0 OpenTenBase V5.21 2026-09-23 21:46:29
如果两行的时间戳不同(例如一个 21:46:29、一个 21:34:xx),说明安装目录里混入了不同批次的产物,无需逐个文件排查,直接回到「一次性干净构建」,从全新解压的源码重新构建一遍即可。后续 initdb 报 postgres was not the same version as initdb 就是这个病因。
六、配置集群
6.1 环境变量:必须写在 .bashrc 最顶部
先解释为什么:Ubuntu 的 ~/.bashrc 开头自带一段「交互检查」,非交互场景直接 return 退出,写在它之后的变量对非交互场景无效:
# Ubuntu ~/.bashrc 开头自带:非交互 shell 直接 return
case $- in
*i*) ;;
*) return;;
esac
而 pgxc_ctl 部署时正是通过 ssh 127.0.0.1 initdb ... 这样的非交互方式执行命令。如果把环境变量写在交互检查之后,远程会话看不到 PATH,就会满屏报错:
initdb: command not found
pg_ctl: command not found
这是源码安装(非 RPM 包)特有的坑:官方镜像里 PATH 是预配好的。
因此要把下面四行加在 opentenbase 用户 ~/.bashrc(即 /data/opentenbase/.bashrc)的最顶部。vim 编辑步骤如下:
# 确认当前用户是 opentenbase(应输出 opentenbase)
whoami
# 若不是,先切换:su - opentenbase
# 用 vim 编辑家目录下的 .bashrc 配置文件
vim ~/.bashrc
注意(实测踩坑):执行
vim ~/.bashrc时可能提示Command 'vim' not found——Ubuntu 22.04 最小化安装并不预装 vim。解决:先执行sudo apt install vim安装(opentenbase 已在 2.3 节加入 sudo 组,输入 opentenbase 自己的密码),再重新执行vim ~/.bashrc;或改用 nano 编辑(按键方式见下文)。
vim 打开后依次执行以下五步:①连按两次小写 g,光标跳至文件第一行;②按大写 I 进入插入模式,左下角显示 – INSERT –;③使用终端粘贴快捷键 Ctrl+Shift+V 粘贴下方四行;④按 Esc 退出插入模式;⑤输入 :wq 并回车,保存并退出。也可改用 nano ~/.bashrc:粘贴后按 Ctrl+O 回车保存,Ctrl+X 退出。需要粘贴的四行如下:
# OpenTenBase 安装根目录(后面第七、八章的命令都依赖它)
export PG_HOME=/data/opentenbase/install/opentenbase_bin_v5.0
# 把 OpenTenBase 的 bin 目录加入 PATH,
# 使 initdb、postgres、pg_ctl、pgxc_ctl 等命令可以直接调用
export PATH=$PG_HOME/bin:$PATH
# 把 lib 目录加入动态库搜索路径
# ${LD_LIBRARY_PATH:-} 表示原变量未设置时取空值,避免报错
export LD_LIBRARY_PATH=$PG_HOME/lib:${LD_LIBRARY_PATH:-}
# 统一语言环境,避免 locale 差异导致初始化或运行异常
export LC_ALL=C
保存后需让配置生效:修改 .bashrc 后,已打开的终端不会自动重新加载,须手动执行一次 source:
# 让配置在当前终端立即生效(每改一次 .bashrc 都要执行一次)
source ~/.bashrc
# 验证 1:应输出 /data/opentenbase/install/opentenbase_bin_v5.0
echo $PG_HOME
# 验证 2:应输出 /data/opentenbase/install/opentenbase_bin_v5.0/bin/pgxc_ctl
which pgxc_ctl
注意:以后每修改一次 .bashrc,都需执行
source ~/.bashrc使其对当前终端生效;新开终端会自动加载,无需再次 source。第七、八章如遇 command not found,应首先确认是否执行过 source。
6.2 集群规划
表 6-1 单机四节点端口规划
| 节点 | 角色 | 主端口 | 数据目录 |
|---|---|---|---|
| gtm | 全局事务管理器 | 20001 | /data/opentenbase/data/gtm |
| cn001 | 协调节点(对外提供 SQL) | 30004 | /data/opentenbase/data/cn001 |
| dn001 | 数据节点 1 | 40004 | /data/opentenbase/data/dn001 |
| dn002 | 数据节点 2 | 40007 | /data/opentenbase/data/dn002 |
每个节点还有一个转发端口和一个连接池端口(例如 cn001 另占 30005、30006),按 6.3 节配置文件中的写法保持递增即可。完整端口清单见第十一章表 11-1。
6.3 pgxc_ctl.conf 完整配置
创建配置目录,并在该目录下新建 pgxc_ctl.conf 文件(注意当前用户必须是 opentenbase,文件属主错了第七章会读不到配置):
# 创建 pgxc_ctl 专用配置目录并进入
mkdir -p /data/opentenbase/pgxc_ctl
cd /data/opentenbase/pgxc_ctl
# 用 vim 新建并编辑配置文件(文件不存在时 vim 会自动创建)
vim pgxc_ctl.conf
vim 按键流程:打开后按 I 进入插入模式 → Ctrl+Shift+V 粘贴下方全部内容 → 按 Esc → 输入 :wq 回车保存。若中途编辑出错,按 Esc 后输入 :q! 回车不保存退出,重新打开文件再编辑。粘贴的内容如下(这是实测可用的完整文件,从 #!/bin/bash 到最后一个 (none) 全部复制):
#!/bin/bash
pgxcInstallDir=/data/opentenbase/install/opentenbase_bin_v5.0
pgxcOwner=opentenbase
defaultDatabase=postgres
pgxcUser=$pgxcOwner
tmpDir=/tmp
localTmpDir=$tmpDir
configBackup=n
configBackupHost=pgxc-linker
configBackupDir=$HOME/pgxc
configBackupFile=pgxc_ctl.bak
myip=127.0.0.1
rundir=/data/opentenbase/data
clusterMaxMachine=1
clusterMaxParallelQuery=10
#---- GTM ----------
gtmName=gtm
gtmMasterServer=$myip
gtmMasterPort=20001
gtmMasterDir=$rundir/gtm
gtmExtraConfig=none
gtmMasterSpecificExtraConfig=none
gtmSlave=n
gtmSlaveServer=$myip
gtmSlavePort=20002
gtmSlaveDir=$rundir/gtm_slave
gtmSlaveSpecificExtraConfig=none
gtmProxyDir=$rundir/gtm_pxy
gtmProxy=n
gtmPxyExtraConfig=none
gtmPxySpecificExtraConfig=(none)
gtmProxyNames=(gtm_pxy1)
gtmProxyPorts=(20003)
gtmProxyServers=($myip)
gtmProxyDirs=($gtmProxyDir)
gtmHostType=(primary)
gtmHostIdx=(0)
#---- Coordinators -------
coordNames=(cn001)
coordPorts=(30004)
coordForwardPorts=(30005)
poolerPorts=(30006)
coordPgHbaEntries=(127.0.0.0/8)
coordMasterServers=($myip)
coordMasterDirs=($rundir/cn001)
coordMaxWALSenders=(4)
coordSlave=n
coordSlaveSync=n
coordSlaveServers=($myip)
coordSlavePorts=(30007)
coordSlavePoolerPorts=(30008)
coordSlaveDirs=($rundir/cn001_slave)
coordArchLogDirs=($rundir/coord_archlog)
coordExtraConfig=coordExtraConfig
cat > $coordExtraConfig <<EOF
listen_addresses = '*'
wal_level = hot_standby
max_connections = 100
shared_buffers = 256MB
max_pool_size = 200
log_destination = 'csvlog'
logging_collector = on
log_directory = 'pg_log'
log_truncate_on_rotation = on
log_filename = 'postgresql-%A-%H.log'
log_rotation_age = 4h
log_rotation_size = 0
EOF
coordSpecificExtraConfig=(none)
coordExtraPgHba=none
coordSpecificExtraPgHba=(none)
#---- Datanodes ---------------------
primaryDatanode=dn001
datanodeNames=(dn001 dn002)
datanodePorts=(40004 40007)
datanodeForwardPorts=(40005 40008)
datanodePoolerPorts=(40006 40009)
datanodePgHbaEntries=(127.0.0.0/8)
datanodeMasterServers=($myip $myip)
datanodeMasterDirs=($rundir/dn001 $rundir/dn002)
datanodeMaxWALSenders=(4 4)
datanodeSlave=n
datanodeSlaveServers=($myip $myip)
datanodeSlavePorts=(40010 40011)
datanodeSlavePoolerPorts=(40012 40013)
datanodeSlaveSync=n
datanodeSlaveDirs=($rundir/dn001_slave $rundir/dn002_slave)
datanodeArchLogDirs=($rundir/dn001_archlog $rundir/dn002_archlog)
datanodeExtraConfig=datanodeExtraConfig
cat > $datanodeExtraConfig <<EOF
listen_addresses = '*'
wal_level = hot_standby
max_connections = 100
shared_buffers = 512MB
max_pool_size = 200
log_destination = 'csvlog'
logging_collector = on
log_directory = 'pg_log'
log_truncate_on_rotation = on
log_filename = 'postgresql-%A-%H.log'
log_rotation_age = 4h
log_rotation_size = 0
EOF
datanodeSpecificExtraConfig=(none)
datanodeExtraPgHba=none
datanodeSpecificExtraPgHba=(none)
# 保存后检查:应能打印出文件的前 3 行(#!/bin/bash 等)
ls -l pgxc_ctl.conf
head -3 pgxc_ctl.conf
有两个细节容易踩坑:第一,所有数组型变量的元素个数必须与节点数一一对应,例如两个 DN 就必须写 datanodeMaxWALSenders=(4 4),只写 (4) 会在启动时报 Number of elements in datanode master definitions are different;第二,coordPgHbaEntries=(127.0.0.0/8) 控制客户端白名单,单机环境保持环回地址段即可,若需要从其它机器连接,把对应网段加进去。
七、部署与启动集群
配置完成后开始部署。确保当前为 opentenbase 用户:
# 切换到 opentenbase 用户
su - opentenbase
# 进入 pgxc_ctl 工作目录
# pgxc_ctl 启动时会自动读取当前目录下的 pgxc_ctl.conf,所以必须先 cd 过来
cd /data/opentenbase/pgxc_ctl
# 启动 pgxc_ctl 交互式命令行
pgxc_ctl
提示符变为 pgxc_ctl> 即表示已进入管理界面。依次执行以下三条子命令,过程中若弹出类似 Continue? 的确认提示,输入 y 回车:
# 1) 将二进制分发到各节点(本机即本机),并生成 pgxc_ctl_bash
# 会把 $PG_HOME 打包,按配置分发到各节点
> deploy all
# 2) 初始化 GTM / CN / DN 四个实例并拉起
# GTM:全局事务管理器;CN:协调节点,对外提供 SQL;DN:数据节点
> init all
# 3) 查看集群状态
> monitor all
monitor all 全部正常时应输出四行 Running:
Running: gtm master
Running: coordinator master cn001
Running: datanode master dn001
Running: datanode master dn002
日常启停:重启虚拟机后集群不会自动拉起,登录 opentenbase 用户执行 pgxc_ctl 后输入 start all 即可(不需要再 deploy/init);关闭前先 stop all(完整步骤见第九章)。另外,刚才的 deploy all 过程中 pgxc_ctl 会在 /data/opentenbase/install 下打包二进制 tar 包,若磁盘紧张可以删掉生成的临时包。
退出方式:退出 pgxc_ctl 管理界面:在
pgxc_ctl>后输入 exit 回车(或按 Ctrl+D),即回到普通命令行。
八、数据库初始化与验收
8.1 连接协调节点
# 使用 psql 连接协调节点(CN)
# -h 127.0.0.1:连接本机
# -p 30004:协调节点端口,与 pgxc_ctl.conf 中的 coordPorts 一致
# -d postgres:连接默认的 postgres 数据库
# -U opentenbase:使用 opentenbase 用户登录
psql -h 127.0.0.1 -p 30004 -d postgres -U opentenbase
连接成功后提示符变为 postgres=#,表示已进入 psql 交互界面。此界面中的 SQL 语句须以英文分号结尾才会执行;psql 自身的元命令以反斜杠开头(如 \c、\l);退出 psql 使用 \q 命令。
8.2 初始化节点组与分片组(源码安装必做)
这是源码安装与官方镜像的又一关键差异:pgxc_ctl 的 init all 不会创建分片映射,直接 CREATE TABLE 会报错 ERROR: default group not defined (42704),补上节点组后再建表会报 please initialize group ... sharding map first。在 psql 里执行以下两条 SQL 即可一劳永逸:
-- 创建默认节点组 default_group,包含两个数据节点 dn001、dn002
-- default_group 是节点组名称;WITH (dn001, dn002) 指定组内的 DN 列表
CREATE DEFAULT NODE GROUP default_group WITH (dn001, dn002);
-- 将默认节点组设置为分片组,初始化全局分片映射
-- 为后续建表的分片规则建立基础映射关系
CREATE SHARDING GROUP TO GROUP default_group;
第一条把两个 DN 组成默认节点组,第二条初始化全局分片映射。如果后续想建显式指定分片的表,语法为 DISTRIBUTE BY SHARD(id) TO GROUP default_group:
-- 建表时通过 DISTRIBUTE BY SHARD(id) 指定按 id 分片
-- TO GROUP default_group 指定分片到 default_group 节点组
CREATE TABLE ... DISTRIBUTE BY SHARD(id) TO GROUP default_group;
8.3 建库建表验收
-- 创建验收用数据库
CREATE DATABASE simple_db;
-- 切换到 simple_db 数据库(提示符会变成 simple_db=#)
\c simple_db
-- 创建测试表 test01,含两个字段:id 和 name
CREATE TABLE test01 (id INT, name TEXT);
-- 插入两行测试数据
INSERT INTO test01 VALUES (1, 'hello'), (2, 'world');
-- 查询 test01 全部数据
-- 能查到 2 行,说明协调节点 → 数据节点 → 存储 全链路打通
SELECT * FROM test01;
8.4 验收结果
三项验收结果如下,均为实测真实运行截图。
验收项一:monitor all 四个节点全部 Running

验收项二:psql 连接成功,SELECT version() 显示 OpenTenBase V5.21

验收项三:simple_db.test01 建表插入查询到 2 行

九、日常使用:重启后启动集群与基础 SQL 操作
安装与验收全部完成后,集群进入日常使用阶段。本章给出两个最常用的流程:一是重启虚拟机后把集群重新拉起来,二是在 psql 里完成建库、建表、查询、修改、删除等基础操作。所有命令均假设第八章的验收已经通过。
9.1 重启虚拟机后的集群启动
虚拟机重启后,GTM、CN、DN 四个进程不会自动拉起(本教程未配置开机自启,如有需要可自行编写 systemd 服务单元,此处不展开),需要手动启动。此前做过的配置都保存在磁盘上,重启后依然有效,无需重做:2.4 节的 SSH 免密、6.1 节写在 .bashrc 最顶部的环境变量如此,第八章创建的数据库、表以及 8.2 节的节点组与分片映射也是如此。同样不需要再执行第七章的 deploy all 与 init all,用 start all 拉起即可。
# 打开终端(Ctrl+Alt+T),切换到 opentenbase 用户(输入它的密码)
su - opentenbase
# 进入 pgxc_ctl 工作目录
# pgxc_ctl 启动时自动读取当前目录下的 pgxc_ctl.conf,必须先 cd 过来
cd /data/opentenbase/pgxc_ctl
# 启动 pgxc_ctl 交互式命令行,提示符变为 pgxc_ctl> 即成功
pgxc_ctl
# 查看集群当前状态:刚开机时应显示四个节点均为 Not running
> monitor all
# 一键拉起全部节点(GTM / CN / DN),无需再 deploy / init
> start all
# 再次确认状态:应输出四行 Running(gtm / cn001 / dn001 / dn002)
> monitor all
# 退出 pgxc_ctl 管理界面,回到普通命令行
> exit
验证标准:monitor all 输出四行 Running 即启动成功,随后即可按 9.3 节连接数据库。若 start all 中途报错或状态长期为 Not running,先按第十章排错表排查——最常见的原因是上次关机前没有 stop all 就直接关闭了虚拟机。
9.2 关闭虚拟机前:干净地停止集群
日常关机前建议先停止集群,让四个节点把内存中的数据正常落盘,避免留下脏数据;停止后再次开机就按 9.1 节的流程启动。若只是暂时不用数据库而不打算关机,同样先 stop all,需要时再 start all。
# 切换到 opentenbase 用户
su - opentenbase
# 进入 pgxc_ctl 工作目录
cd /data/opentenbase/pgxc_ctl
# 启动 pgxc_ctl 管理界面
pgxc_ctl
# 停止全部节点(GTM / CN / DN 依次关闭)
> stop all
# 确认已全部停止:应显示四个 Not running
> monitor all
# 退出管理界面,之后即可正常关闭虚拟机
> exit
说明:先 stop all 再关机可避开绝大多数启动异常。若曾强制关机导致 start all 失败,按第十章排错表处理;注意其中「清空 /data/opentenbase/data/* 重新 init」的做法会删除全部数据,仅适用于库里没有重要数据的场合。
9.3 基础 SQL 操作:建库、建表、增删改查
以下操作在集群启动后进行,先按 8.1 节连接协调节点,再依次执行。SQL 语句以英文分号 ; 结尾才会执行;以 -- 开头的是注释,不用输入。示例使用新的练习数据库 demo_db,与 8.3 节的验收数据互不影响;建表依赖 8.2 节创建的 default_group,若当时跳过了那两条 SQL,先回 8.2 节补做。
# 连接协调节点(CN),成功后提示符变为 postgres=#
# -h/-p:本机 30004 端口;-d:默认数据库;-U:登录用户
psql -h 127.0.0.1 -p 30004 -d postgres -U opentenbase
-- 创建练习用数据库
CREATE DATABASE demo_db;
-- 切换到 demo_db(提示符变为 demo_db=#)
\c demo_db
-- 建表:三个字段;DISTRIBUTE BY SHARD(id) 表示按 id 字段分片
-- TO GROUP default_group:分片落到 8.2 节创建的默认节点组(dn001、dn002)
CREATE TABLE student (id INT, name TEXT, score INT) DISTRIBUTE BY SHARD(id) TO GROUP default_group;
-- 插入三行数据
INSERT INTO student VALUES (1, '张三', 90), (2, '李四', 85), (3, '王五', 78);
-- 查询全部数据(应返回 3 行)
SELECT * FROM student;
-- 条件查询 + 排序:80 分及以上,按分数从高到低
SELECT name, score FROM student WHERE score >= 80 ORDER BY score DESC;
-- 修改:把 id 为 2 的分数改为 95
UPDATE student SET score = 95 WHERE id = 2;
-- 删除行:删掉 id 为 3 的记录
DELETE FROM student WHERE id = 3;
-- 查看表结构(字段名与类型)
\d student
-- 删除表
DROP TABLE student;
-- 删除数据库:不能删除当前正在连接的库,先切回 postgres
\c postgres
-- 删除 demo_db
DROP DATABASE demo_db;
-- 退出 psql,回到命令行
\q
说明:以上是最基础的入门操作。OpenTenBase 兼容大部分 PostgreSQL 语法,普通建表、查询的写法与 PostgreSQL 一致;分布式相关特性(分片键的选择、复制表等)建议先查阅官方文档(见 11.3 节)再用于实际数据。
十、常见问题与排错
表 10-1 全流程排错表
| 问题 | 原因 | 解决 |
|---|---|---|
| git clone 报 GnuTLS recv error / Connection refused | GitHub 连接不稳定 | 改用第三章的 codeload 直链或 gitcode 镜像 |
| configure: error: zstd library not found | configure 硬编码 BINARYLIBS 路径 | 第四章问题一 sed 补丁 + 安装 libzstd-dev liblz4-dev |
| conflicting types for ‘getmissingattr’ | char bool 与 OpenSSL 3.0 的 _Bool 冲突 | 第四章问题二给 c.h 加 #include <stdbool.h> |
| postgres was not the same version as initdb | 多次 configure 混装了不同时间戳的产物 | 按「一次性干净构建」从全新解压一次性重建 |
| initdb/pg_ctl: command not found(init all 时) | 非交互 SSH 看不到 PATH | 环境变量写到 .bashrc 最顶部(6.1 节)并确认 source 过 |
| Number of elements in datanode master definitions are different | 数组元素个数与节点数不符 | datanodeMaxWALSenders 等改成每节点一个元素(6.3 节) |
| ERROR: default group not defined | pgxc_ctl 不会自动创建分片映射 | 执行 8.2 节的两条 CREATE … GROUP SQL |
| monitor all 显示 Not running | 上次初始化失败留下脏数据 | 停掉进程后清空 /data/opentenbase/data/*,重新 deploy all + init all |
| su - opentenbase 报「认证失败」 | 密码输错(输入时不回显) | 重新输入;忘记密码用 sudo passwd opentenbase 重设 |
| cat ~/.ssh/id_rsa.pub >> authorized_keys 报 No such file or directory,ssh 仍要密码 | 跳过了 ssh-keygen,~/.ssh 目录与密钥对都不存在 | 补执行 ssh-keygen 生成密钥对,再重做 cat 追加与 chmod 600(2.4 节) |
| 刚改完 .bashrc,命令仍 command not found | 忘了 source,或变量写在交互检查之后 | source ~/.bashrc;确认四行 export 在文件最顶部(6.1 节) |
| vim 里按错键、画面混乱 | 插入/普通模式混淆 | 先按 Esc,再输入 :q! 回车不保存退出,重新打开重来 |
| Command ‘vim’ not found(编辑 .bashrc / pgxc_ctl.conf 时) | Ubuntu 22.04 未预装 vim | sudo apt install vim;或改用 nano(Ctrl+O 回车保存,Ctrl+X 退出) |
| 在 psql 中无法退出 | psql 有独立的元命令体系 | 输入 \q 回车退出;pgxc_ctl 中则输入 exit |
| pgxc_ctl 找不到配置 / 配置不生效 | 未在 pgxc_ctl.conf 所在目录启动 | 先 cd /data/opentenbase/pgxc_ctl 再运行 pgxc_ctl(第七章) |
| Could not get lock /var/lib/dpkg/lock-frontend | unattended-upgrades 后台升级占用 apt | 等待其自然结束(ps -p <PID> 确认),不要强删锁 |
一个小技巧:如果需要在脚本里得到带 postgres=# 提示符的交互式输出(比如截图存档),用 printf '...' | ssh -tt opentenbase@127.0.0.1 psql ... 强制分配终端;直接管道进 psql 的非交互输出是没有提示符的。
十一、附录
11.1 完整端口清单
表 11-1 端口占用清单
| 端口 | 用途 |
|---|---|
| 20001 | GTM 主节点 |
| 20003 | GTM Proxy(本方案未启用) |
| 30004 | CN cn001 对外提供 SQL(psql 连这里) |
| 30005 | CN 转发端口 |
| 30006 | CN 连接池端口 |
| 40004 / 40005 / 40006 | DN dn001 主 / 转发 / 连接池 |
| 40007 / 40008 / 40009 | DN dn002 主 / 转发 / 连接池 |
11.2 目录结构
全部安装完成后,/data/opentenbase 的目录结构如下:
/data/opentenbase/
├── OpenTenBase/ # 源码(已打两处补丁)
├── install/
│ └── opentenbase_bin_v5.0/ # 编译安装产物(bin lib include share)
├── pgxc_ctl/
│ └── pgxc_ctl.conf # 集群配置(第六章)
└── data/
├── gtm/ # GTM 实例
├── cn001/ # 协调节点
├── dn001/ # 数据节点 1
└── dn002/ # 数据节点 2
11.3 参考链接
- OpenTenBase 官方仓库(GitHub):https://github.com/OpenTenBase/OpenTenBase
- OpenTenBase 官方主页:https://www.opentenbase.org/
- OpenTenBase 官方文档:https://docs.opentenbase.org/
- 国内镜像(gitcode):https://gitcode.com/OpenTenBase/OpenTenBase
- 本教程独立仓库(含 Word 原稿):https://github.com/Bogucheese/opentenbase-ubuntu-install-guide
11.4 结语
至此,Ubuntu 22.04 环境下的源码编译与单机分布式集群部署全部完成。安装过程中的两项关键经验——修改前备份、修改后立即验证——同样适用于后续的版本升级与问题排查。遇到本教程未覆盖的报错时,建议优先完整阅读报错原文,多数问题的线索位于前两行。