从零构建企业级数据库协作环境:阿里云 Linux 3 部署 MySQL、Redis、PostgreSQL 与 pgvector 全指南 | 星雨笔录
以阿里云 ECS 和 Alibaba Cloud Linux 3 为基础,系统讲解服务器初始化、SSH、安全组、DNF 与 systemd,再分别完成 MySQL 8.4、Redis、PostgreSQL 17 及 pgvector 的安装、配置、测试、运维。进一步以真实研发团队为模型,设计开发、测试、预发布与生产环境隔离,PostgreSQL 分层角色授权、团队成员生命周期管理、Flyway 数据库迁移、CI/CD、测试数据治理、备份恢复和故障排查。教程既能用于个人实验,也能迁移到多人协作、Java 微服务、Python AI Agent 和企业级项目。
一、先建立正确认知:数据库“共享”不等于“团队协作”
1.1 三种经常被混为一谈的共享
假设团队在开发一个“知识管理 + AI 问答”系统,包含前端、Java API、Python Agent、数据库和缓存。
| 共享对象 | 实际含义 | 正确工具 | 常见误区 |
|---|---|---|---|
| 源代码共享 | 所有人能获取同一份经过审查的代码 | Git、Pull Request | 共享数据库不会同步代码 |
| 数据库结构共享 | 所有环境知道自己正在使用哪个 Schema 版本 | Flyway / Liquibase、SQL 迁移文件 | 在 IDEA 手动建表,通知同事“你也加一下字段” |
| 数据共享 | 团队针对一组受控测试数据开展联调 | 集成环境数据库、测试数据集 | 把所有开发人员的每次实验都塞进同一个数据库 |
一句话核心:数据库负责存储数据,Git 管理代码,数据库迁移工具管理结构变化,CI/CD 管理变化如何进入不同环境。
1.2 一个完整的软件交付链
产品需求 / Issue
↓
开发者创建 feature/* 分支
↓
本地数据库 / 临时测试数据库
↓
提交代码 + 数据库迁移 SQL + 自动化测试
↓
Pull Request / Code Review
↓
CI:编译、单测、数据库集成测试、迁移校验
↓
合并主干
↓
共享 DEV / 集成环境自动迁移与联调
↓
TEST / QA 验收
↓
STAGING 预发布验证(接近生产)
↓
PROD:审批、备份、迁移、滚动发布、监控
日常开发中,可以有一个共享开发库,但它不是所有工作的唯一数据库。更成熟的做法是“个人隔离开发 + 自动化集成验证 + 共享联调环境”。
1.3 本文包含三种可选部署形态
| 维度 | 学习 / 演示 | 小型研发团队 | 企业生产 |
|---|---|---|---|
| 数据服务部署 | 单台 ECS | 独立数据库 ECS / 托管服务 | 托管 RDS、可用区冗余、容灾 |
| 访问网络 | 临时公网 + IP 白名单 + TLS | VPC / VPN / 堡垒机 | 私网、零信任、严格审计 |
| 应用运行 | 本地 IDEA / Python | 本地 + DEV 服务 | K8s / ECS / 容器或其他平台 |
| 数据库账号 | 独立开发账号 | 成员账号 + 服务账号 + 迁移账号 | IAM 集成、凭据管理、自动轮换 |
| 表结构变更 | SQL 文件入 Git | Flyway + CI | 评审、灰度、可观测、可恢复 |
| 备份 | 手工备份 | 定时备份与恢复测试 | RPO / RTO、PITR、异地容灾 |
决策优先级:先根据业务和团队规模选部署模式,再决定软件如何安装。安装成功只是起点,不是企业级的终点。
二、环境规划:CPU、内存、端口和产品选型
2.1 这三类数据库究竟负责什么?
| 组件 | 数据模型 | 擅长解决的问题 | 常用默认端口 |
|---|---|---|---|
| MySQL 8.4 LTS | 关系型 | 业务表、事务、查询、经典 Java 后端 | 3306 |
| PostgreSQL 17 | 关系型 + JSONB 等 | 复杂业务查询、GIS、JSONB、扩展生态 | 5432 |
| pgvector | PostgreSQL 扩展 | Embedding 存储、相似度查询、向量索引 | 不单独监听端口 |
| Redis | 内存数据结构 | 缓存、短期会话状态、计数、限流等 | 6379 |
不要简单理解成“Python 必须用 PostgreSQL”或“AI 一定要 pgvector”。普通 LLM API 对话不要求向量库;文档检索、RAG、语义搜索、长期记忆才常常需要向量检索能力。MySQL 与 PostgreSQL 并非必须同时部署,两者可以是独立可选方案。
2.2 单台 2 GiB ECS 的边界
示例规格:2 vCPU、2 GiB RAM、40 GiB 系统盘。Linux 内可能显示约 1.8 GiB 可用物理内存,另有可能存在 Swap。
建议:
- 用来学习时可以分别安装 MySQL、Redis、PostgreSQL;不使用的重型数据库先停止。
- Redis 若仅作为轻量缓存,可先从
128mb上限开始观察;maxmemory不等于进程的全部 RSS 内存上限。 - PostgreSQL 连接数、缓存、查询内存必须结合应用连接池调整;2 GiB 不适合高并发生产流量。
- Java、Python Agent、向量生成模型优先在开发电脑、其他计算实例或外部模型服务运行。
- 数据量增长后,应把数据盘、备份、监控和计算资源分离;生产更推荐阿里云 RDS PostgreSQL/MySQL 等托管服务。
2.3 网络拓扑:优先选私网,公网直连只作为受控替代
推荐:
公司/开发者设备
│
VPN / SSO / 跳板机 / 堡垒机
│
阿里云 VPC 私有网络
├─ API 服务(Java / Python)
├─ PostgreSQL 或 MySQL(只开放私网)
└─ Redis(只开放私网)
可用于临时教学演示的公网方案:
开发者设备 --TLS--> 具备公网 IP 的 ECS
├─ PostgreSQL : 5432
└─ MySQL : 3306
云安全组仅允许已批准的来源 IP /32;数据库仍要求账号认证、TLS。
Redis 不应直接暴露到不可信公网。
补充:listen_addresses='*' 或 bind-address=0.0.0.0 只是“服务监听所有网卡”,不是授权任意用户访问;数据库认证、Linux 防火墙、阿里云安全组是不同层次。
2.4 端口设计表
| 端口 | 服务 | 开发环境 | 生产环境建议 |
|---|---|---|---|
| TCP 22 | SSH | 仅授权管理来源 | 通过堡垒机或可信管理网络 |
| TCP 3306 | MySQL | 私网优先,受控公网备选 | 私网 |
| TCP 5432 | PostgreSQL | 私网优先,受控公网备选 | 私网 |
| TCP 6379 | Redis | 回环 / VPC | 私网 + ACL / TLS(视产品和版本) |
| TCP 80/443 | HTTP/HTTPS | 按应用需要开放 | 通过 HTTPS、WAF/网关 |
| TCP 33060 | MySQL X Plugin | 用不到时可关闭 | 按业务需求,勿无意公开 |
三、从零准备阿里云 ECS 与 Alibaba Cloud Linux 3
3.1 在阿里云控制台创建实例
进入阿里云 ECS → 创建实例,按顺序选择:
- 地域与可用区:靠近实际用户或团队的地域;生产应考虑多可用区。
- 实例规格:实验可以 2 vCPU / 2 GiB;按负载估算生产规格。
- 镜像:Alibaba Cloud Linux 3(64 位)。
- 存储:系统盘与业务数据盘规划;生产尽量独立数据盘并启用云盘快照。
- 网络:选择 VPC、交换机;仅教学公网直连才配置公网地址。
- 登录认证:优先 SSH 密钥;若使用密码,设置高强度密码且不发送给他人。
- 安全组:只配置必要入方向规则。不要为了“省事”开放所有端口。
- 创建后确认实例状态“运行中”,记录实例 ID、地域、私网 IP、公网 IP(若有),记录在团队的基础设施台账中,不要公开发布真实地址和凭据。
本章使用通用示例:
操作系统:Alibaba Cloud Linux 3
文档示例公网 IP:203.0.113.10
示例可信办公出口:198.51.100.20/32
SSH:22
数据库开发环境名称:collab_dev
上面 203.0.113.0/24、198.51.100.0/24 是文档示例网段,不是真实服务地址。实际执行时需要替换为控制台显示的真实值。
3.2 安全组如何填
ECS → 网络与安全组 → 安全组 → 入方向:
| 优先级 | 协议 | 目的端口 | 来源 | 原因 |
|---|---|---|---|---|
| 1 | TCP | 22 | 已批准的办公 / VPN IP/32或 CIDR |
SSH 管理 |
| 1 | TCP | 5432 | 已批准的开发出口 IP/32(仅有公网直连需求时) |
PostgreSQL 访问 |
| 1 | TCP | 3306 | 已批准的开发出口 IP/32(仅有公网直连需求时) |
MySQL 访问 |
| — | TCP | 6379 | 不创建公网规则 | Redis 留在可信网络内 |
192.168.x.x一般是电脑的局域网地址,不是云安全组识别的公网出口地址。0.0.0.0/0表示所有 IPv4 来源,用于数据库管理端口会显著扩大攻击面,不适合作为企业默认配置。- 若开发人员公网 IP 不固定,推荐 VPN / 零信任接入 / 跳板机,而不是让安全组对全球开放。
- 公网 IPv6 还要检查 IPv6 入方向规则,不能只看 IPv4。
3.3 使用 FinalShell 登录 Linux
在 Windows 打开 FinalShell,新增 SSH 连接:
连接名称:dev-db-ecs
主机:<ECS_PUBLIC_IP 或 PRIVATE_IP>
端口:22
认证:SSH 私钥(推荐)/ 密码(临时实验)
用户名:由云镜像与账户配置决定,例如 root 或非特权账户
首次连接时核验 SSH 主机指纹。连接失败按顺序查:主机地址 → 安全组 TCP 22 → 本地网络 → sshd → 操作系统防火墙 → 用户名/凭据。TCP timeout 与 Access denied 是不同故障阶段。
企业更建议为每位工程师创建独立 Linux 账号或接入集中身份管理,不共享 root 凭据。日常操作优先 sudo,重要管理动作留审计记录。
3.4 第一轮环境自检:命令、含义和预期
进入 Linux Shell 后执行:
whoami # 当前 Linux 用户
cat /etc/os-release # 发行版与版本
uname -r # 内核版本
uname -m # x86_64 / aarch64 等 CPU 架构
free -h # 内存
swapon --show # Swap 是否配置
df -h # 磁盘空间
lsblk # 云盘与分区
ip -br addr # 网卡地址
ss -lntp # 正在监听的 TCP 端口
systemctl --failed # 启动失败的 systemd 服务
dnf repolist # 当前软件源
核心原理:RPM 是软件包格式;DNF 负责查找、下载并解析依赖;systemd 管理 Linux 服务;ss 查询内核网络套接字;云安全组在云网络层控制流量。
例如:
ss 显示 127.0.0.1:5432 → PostgreSQL 只监听本机 IPv4
ss 显示 0.0.0.0:5432 → PostgreSQL 监听所有 IPv4 网卡
ss 显示 [::]:5432 → 可能监听 IPv6(及系统相关的双栈行为)
这些输出只描述监听,不证明最终的远程权限或加密是否正确。
3.5 安装常用诊断工具
dnf makecache
dnf install -y vim curl wget tar unzip bind-utils iproute openssl
如果仓库连接失败,不要一股脑关闭软件包签名验证。先检查 DNS、网络、时间同步、仓库 URL 和证书。
3.6 Linux 防火墙:三道门
云安全组(云平台 VPC 层)
↓
Linux 防火墙 firewalld / nftables
↓
数据库监听与认证规则
检查:
systemctl is-active firewalld
firewall-cmd --list-all # 仅 firewalld 正在运行时使用
nft list ruleset # 检查 nftables(若系统使用)
不要直接关闭整台服务器防火墙来排错。如果需要开放端口,先明确可信来源,在相应 zone 添加限制规则并验证 SSH 不会失联。生产建议通过 IaC(如 Terraform)集中管理安全组。
3.7 可选:Swap 的意义和风险
Swap 是磁盘上的交换空间,可以缓解暂时的内存峰值,但磁盘比 RAM 慢得多,而且持续换页会显著拖慢数据库。
先检查:
free -h
swapon --show
如果已经存在可用 Swap,不要重复创建。对小型实验机,可以评估后增加 Swap;但“开启 Swap”不等于解决 OOM 根因。企业级数据库仍应靠内存容量规划、连接池和慢 SQL 优化。
3.8 学会看终端提示符,避免把 Linux 命令写成 SQL
| 提示符 | 所处环境 | 示例命令 |
|---|---|---|
[root@host ~]# |
Linux Shell | systemctl status postgresql-17 |
[postgres@host ~]$ |
Linux Shell | psql |
postgres=# |
PostgreSQLpsql |
SELECT version(); |
mysql> |
MySQL 客户端 | SHOW DATABASES; |
在 psql 里执行 su - postgres 会提示 syntax error,因为它是 Linux Shell 命令,不是 SQL。
四、MySQL 8.4 LTS:从安装到可用的完整流程
适用场景:Java / Spring Boot 业务项目、关系型事务系统。若项目最终选用 PostgreSQL 作为唯一主库,本章可作为独立的部署技能练习;不要求在资源有限的 ECS 上同时常驻 MySQL。
4.1 检查系统状态,防止覆盖既有环境
rpm -qa | grep -Ei 'mysql|mariadb'
command -v mysql || true
ss -lntp | grep -E ':(3306|33060)\b' || true
systemctl list-unit-files | grep -Ei 'mysql|mariadb' || true
已运行生产数据库时不要直接执行初始化、覆盖配置或删除目录。先备份、确认数据目录与业务依赖。
4.2 配置 MySQL 官方仓库
本次实践使用过 EL8 官方 MySQL 8.4 仓库 RPM;在 Alibaba Cloud Linux 3 上需额外关注兼容包、模块和发行版差异。仓库 RPM 文件名可能更新,使用前可从官方页面核对:https://dev.mysql.com/downloads/repo/yum/。
在确认架构为 x86_64、仓库可用的前提下,示例命令:
# 某些 Alibaba Cloud Linux 3 环境需要兼容依赖;如无该包,先查具体依赖
dnf install -y compat-openssl10
# 下面是实践时使用的 8.4 EL8 仓库配置包;更新时以官网为准
rpm -Uvh https://repo.mysql.com/mysql84-community-release-el8-1.noarch.rpm
# 若 dnf 显示系统原生 MySQL 模块与官方仓库冲突,再执行:
dnf module disable mysql -y
# 确认目标软件包和依赖
dnf list --available mysql-community-server
dnf install -y mysql-community-server
为什么要禁用系统模块? 同一软件名可能由操作系统模块流与第三方仓库分别提供;不处理会导致版本混用或依赖冲突。如果系统没有 mysql 模块,看到“没有匹配模块”时不要强行修改其他仓库。
4.3 启动、开机自启、检查端口
systemctl enable --now mysqld
systemctl is-active mysqld
mysql --version
systemctl status mysqld --no-pager
ss -lntp | grep ':3306'
active 才表示服务运行。只安装仓库配置 RPM 并不会产生 mysqld.service,要先安装 mysql-community-server。
4.4 初始化管理员账户
MySQL 官方 RPM 首次初始化可能生成临时 root 密码:
# 输出含管理员临时凭据;只在自己可信的终端查看
sudo grep 'temporary password' /var/log/mysqld.log | tail -n 1
随后:
mysql_secure_installation
按提示输入MySQL 管理员临时密码(不是 Linux SSH 密码),设置新密码,删除匿名用户和无用测试库,并禁用不必要的管理员远程登录。
测试:
mysql -u root -p
进入 mysql> 后:
SELECT VERSION();
SHOW VARIABLES LIKE 'validate_password%';
若提示 ERROR 1819,说明新密码未满足当前验证策略:调整密码强度,不要为了省事关闭密码校验。若提示 ERROR 1045,优先核对账号、临时密码及是否将两条命令拼接到一行。
口令安全:不要输入 mysql -uroot -p真实密码,这种写法可能暴露在 Shell 历史、进程信息或共享屏幕里。密码也不要上传到 Git。
4.5 创建示例项目数据库
在 mysql> 执行:
CREATE DATABASE IF NOT EXISTS collab_dev
CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
SHOW DATABASES;
utf8mb4:支持完整 Unicode,包括常见 Emoji。COLLATE:影响字符比较、排序及大小写敏感性。- 数据库名应结合环境,例如
project_dev、project_test,不要所有环境共用同一个库名。
4.6 MySQL 的企业级账号模型
不要按“谁来开发就直接授予整个实例 root”设计权限。推荐至少区分:
| 类型 | 示例 | 权限范围 |
|---|---|---|
| DBA 管理员 | root/ 数据库管理员 |
实例级管理,极少使用 |
| 结构迁移服务账号 | svc_mysql_migrator |
目标数据库 DDL 与必要 DML |
| 应用运行账号 | svc_mysql_app |
SELECT / INSERT / UPDATE / DELETE |
| 只读分析账号 | svc_mysql_reader |
SELECT |
| 开发者个人账号 | dev_member_01 |
限定开发库、限定来源,按岗位分配 |
示例:下面 SQL 中的 <...> 是明确的占位符,不能原样当作实际账号密码;10.20.% 是示例私网来源模式,必须按真实 VPC 网段调整。密码在私密管理会话中输入,使用凭据管理服务存储和分发,尽量避免留在 SQL 历史及录屏中。
-- 以下是一种最小权限设计:只供示意,不在生产中直接复制占位符。
CREATE USER 'svc_mysql_app'@'10.20.%'
IDENTIFIED BY '<STRONG_SECRET_FROM_VAULT>' REQUIRE SSL;
CREATE USER 'svc_mysql_reader'@'10.20.%'
IDENTIFIED BY '<DIFFERENT_STRONG_SECRET>' REQUIRE SSL;
GRANT SELECT, INSERT, UPDATE, DELETE ON collab_dev.*
TO 'svc_mysql_app'@'10.20.%';
GRANT SELECT ON collab_dev.*
TO 'svc_mysql_reader'@'10.20.%';
SHOW GRANTS FOR 'svc_mysql_app'@'10.20.%';
MySQL 的 'username'@'host' 是账号身份的一部分,'app'@'localhost' 和 'app'@'%' 可以是两个不同账号。企业生产更适合显式可信来源;通配符 % 不应成为默认选择。
迁移账号需要独立审查与更高权限,交给 CI/CD 使用,不要将迁移账号密码配置到普通 Web 服务里。
4.7 MySQL 监听地址、TLS 与 X Plugin
先备份配置并查看:
cp -a /etc/my.cnf /etc/my.cnf.bak
cat /etc/my.cnf
选择一种模式:
[mysqld]
# 同机访问或 SSH 隧道
bind-address=127.0.0.1
或仅当必须通过 VPC 网卡、可信公网远程访问时:
[mysqld]
# 监听所有 IPv4 网卡;不代表可以关闭防火墙
bind-address=0.0.0.0
require_secure_transport=ON
# 只有业务不依赖 MySQL X Protocol 时才考虑关闭:
mysqlx=OFF
注意在同一配置段不要放置相互冲突的多个 bind-address。MySQL X Plugin 常见端口为 33060,不需要时可以关闭;若业务依赖它则不能关闭。
systemctl restart mysqld
systemctl is-active mysqld
ss -lntp | grep -E ':(3306|33060)\b'
TLS 需要区分两件事:
REQUIRE SSL或require_secure_transport=ON:要求加密传输。- 客户端
VERIFY_CA/VERIFY_IDENTITY:验证数据库服务器的证书与身份,降低假服务器和中间人风险。
生产必须管理可信 CA、证书续期与服务器名称;不要把“已加密”误当成“证书已可信验证”。
4.8 验证和切换到 PostgreSQL 时怎样保留 MySQL
mysql -u root -p -e 'SELECT VERSION();'
如需暂时关闭,释放小规格 ECS 内存:
systemctl disable --now mysqld
systemctl is-active mysqld # 预期 inactive
这不会自动删除 MySQL 数据文件。停止服务 ≠ 备份 ≠ 卸载。对外提供服务之前确认所有应用已切换数据库连接。
五、Redis:缓存、数据过期、内存管理与安全
5.1 Redis 在系统架构里的位置
HTTP 请求 → Java/Python API
├─ Redis 查询缓存(命中→快速返回)
└─ PostgreSQL/MySQL 业务查询(未命中→查询并回填)
常见用途:热点查询缓存、令牌/短期状态、计数、限流和某些轻量队列。是否能丢失数据,是选择淘汰与持久化策略的首要前提:不允许丢失的重要任务与消息不能随便使用缓存淘汰策略。
5.2 在 Alibaba Cloud Linux 3 安装
rpm -qa | grep -i redis
command -v redis-server || true
dnf list --available redis
# 确认有软件包再安装
dnf install -y redis
不同仓库的实际服务名称可能不同:
systemctl list-unit-files | grep -i redis
systemctl enable --now redis
systemctl is-active redis
redis-server --version
redis-cli ping
预期 redis-cli ping 返回 PONG,只说明基础可用。若 Redis 配了 ACL/密码,需要按实际认证方法测试。
5.3 限制内存、选择淘汰策略
在仅承担缓存、允许旧数据被淘汰的小型演示实例中:
redis-cli CONFIG SET maxmemory 128mb
redis-cli CONFIG SET maxmemory-policy allkeys-lru
redis-cli CONFIG GET maxmemory
redis-cli CONFIG GET maxmemory-policy
如果 Redis 正常采用配置文件启动,可尝试:
redis-cli CONFIG REWRITE
若提示失败,先通过 systemctl cat redis 找到实际配置文件及启动参数,然后在文件中设置:
maxmemory 128mb
maxmemory-policy allkeys-lru
几个容易忽视的事实:
allkeys-lru近似淘汰较少使用的键,适用于纯缓存;并不是业务队列的通用策略。noeviction在内存用尽时可能拒绝写入,适合部分不能随便淘汰的数据,但仍需规划容量。maxmemory不是 Redis 进程 RSS 上限,内存碎片、连接、持久化等仍消耗额外内存。- 不同服务的 Redis 缓存键用统一前缀,例如
project:module:entity:id。 - 应配合 TTL、监控命中率、内存、淘汰次数和延迟,不只设置一个固定数字。
5.4 检查 Redis 的监听与安全
ss -lntp | grep ':6379'
redis-cli CONFIG GET bind
redis-cli CONFIG GET protected-mode
redis-cli INFO memory
开发默认建议:
bind 127.0.0.1 -::1
protected-mode yes
上面 -::1 在部分 Redis 配置中表示 IPv6 回环不可用时不导致启动失败;具体支持与实际配置以安装版本为准。若服务器内部其他机器需要 Redis,应改用可信私网接口 + 安全组 + ACL/TLS,不要裸露 6379 到互联网。
需要远程调试时,推荐 SSH 端口转发、VPN 或应用管理接口。不要把“没有重要数据”当成允许 Redis 公网无认证的理由。
5.5 Redis 最小实践
redis-cli
进入 Redis 客户端:
SET project:demo:hello "Hello Redis" EX 60
GET project:demo:hello
TTL project:demo:hello
DEL project:demo:hello
EX 60:设置 60 秒过期;TTL:查看过期时间;DEL:删除指定键。生产调试不要随手执行 FLUSHALL。
5.6 多服务 Redis 协作怎么治理
推荐“应用有自己前缀和权限边界”,避免所有服务共用一个高权限 Redis 密码。可以利用 Redis ACL 限定身份、命令和键模式;关键业务使用独立实例。不要认为 Redis 的逻辑数据库编号(如 DB 0/1)就是强隔离手段。
Redis 若承担缓存:要定义失效策略、缓存穿透/击穿/雪崩应对;若承担持久队列:要重新评估 AOF/RDB、消费确认、幂等、故障恢复乃至更合适的消息中间件。
六、PostgreSQL 17 + pgvector:从安装到向量查询
6.1 为什么选择 17,而不是系统默认 13?
在部分 Alibaba Cloud Linux 3 环境中:
dnf list --available 'postgresql*'
系统仓库可能提供 PostgreSQL 13 系列,而项目可能需要更新的长期维护大版本及特定 pgvector 软件包。不能只因为旧版“能装”就认为它适合新项目;版本选型应综合支持周期、插件兼容、驱动和升级路径。
6.2 安装 PGDG 社区仓库(需要留意发行版兼容性)
PGDG 是 PostgreSQL 社区的软件包仓库。Alibaba Cloud Linux 3 不是 PGDG 官方直接列出的标准受支持发行版;在兼容性允许的情况下可尝试 EL8 仓库,但生产场景应优先选择明确受支持的平台或托管数据库。
基于 x86_64、EL8 兼容环境的实践示例:
dnf install -y \
https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm
如果出现类似错误:
... /redhat/rhel-3-x86_64/repodata/repomd.xml
HTTP 404 Not Found
原因:仓库的 $releasever 被识别成 Alibaba Cloud Linux 的 3,而不是我们期望的 EL8 兼容路径。
仅在确认该机器使用 EL8 兼容 PGDG 仓库且报错确为上述原因时,修正 PGDG 专属配置文件:
cp -a /etc/yum.repos.d/pgdg-redhat-all.repo \
/etc/yum.repos.d/pgdg-redhat-all.repo.bak
# 只改 PGDG 仓库,不能改整个操作系统的 releasever
sed -i 's/\$releasever/8/g' \
/etc/yum.repos.d/pgdg-redhat-all.repo
dnf clean metadata
dnf makecache
之后验证目标包是否确实存在:
dnf list --available postgresql17-server pgvector_17
如果仓库包不存在、GPG 签名异常或依赖不兼容,不要使用 --nogpgcheck、--skip-broken 硬装;应重新选择支持的发行版/仓库或使用经过验证的替代部署方案。
6.3 安装 PostgreSQL 17 与 pgvector
# 存在冲突模块时才需要;具体以当前系统仓库为准
dnf module disable postgresql -y
dnf install -y postgresql17-server pgvector_17
/usr/pgsql-17/bin/postgres --version
rpm -q pgvector_17
软件包显示安装成功不代表数据库实例已经初始化,也不等于某个业务库自动启用了 vector 扩展。
6.4 初始化 PostgreSQL 集群、启动服务
首次安装且确定目标数据目录没有已有数据库时:
/usr/pgsql-17/bin/postgresql-17-setup initdb
systemctl enable --now postgresql-17
systemctl is-active postgresql-17
ss -lntp | grep ':5432'
常见默认数据目录:/var/lib/pgsql/17/data/。不要对一个已经存储业务数据的目录重复 initdb。
6.5 正确登录 PostgreSQL
在 Linux root Shell:
su - postgres
psql
进入 postgres=# 后:
SELECT version();
SHOW data_directory;
SHOW config_file;
SHOW hba_file;
退出 psql 使用 \q,再用 Linux exit 返回原来的 Shell;如果误把 su、systemctl 输进 psql,它们会被解析为错误 SQL。
6.6 创建业务库与 pgvector 扩展
在 postgres=#:
CREATE DATABASE collab_dev WITH ENCODING 'UTF8' TEMPLATE template0;
\c collab_dev
CREATE EXTENSION IF NOT EXISTS vector WITH SCHEMA public;
SELECT extname, extversion
FROM pg_extension
WHERE extname = 'vector';
原理:pgvector_17 是操作系统层面的扩展包;CREATE EXTENSION vector 是在某一个 PostgreSQL 数据库内部激活类型、函数和操作符。不同数据库要按需分别激活。
6.7 第一个 Embedding 向量演示
SELECT '[1,2,3]'::vector <-> '[2,3,4]'::vector AS l2_distance;
输出应接近:
1.7320508075688772
这里 <-> 表示欧氏距离。真实 RAG 的 Embedding 通常是数百到数千维,维度取决于模型;不能混用不同 Embedding 模型生成的向量后直接当作可比较语义。
创建一个用于教学的最小表(这一小段是管理员验证语法用的独立演示,不是下面企业权限模型的迁移流程):
CREATE TABLE public.vector_demo (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
content TEXT NOT NULL,
embedding vector(3) NOT NULL
);
INSERT INTO public.vector_demo (content, embedding) VALUES
('文档 A', '[1,2,3]'),
('文档 B', '[2,3,4]'),
('文档 C', '[8,9,10]');
SELECT id, content, embedding <-> '[1,2,3]'::vector AS distance
FROM public.vector_demo
ORDER BY embedding <-> '[1,2,3]'::vector
LIMIT 2;
生产数据应使用清晰的 Embedding 模型版本、维度、切分策略、租户隔离规则、过滤字段、重建索引方案;根据数据规模和延迟目标再选 HNSW/IVFFlat 等索引,而不是一开始就机械加索引。
七、PostgreSQL 网络访问、SCRAM 认证与 TLS
7.1 一次远程连接会经过哪些关卡?
IDEA / 应用
↓ DNS / IP
云安全组(源地址 + 5432)
↓
系统防火墙
↓
PostgreSQL listen_addresses
↓
TLS 握手(证书 + 加密)
↓
pg_hba.conf(连接来源 / 数据库 / 用户 / 方法)
↓
密码认证 SCRAM-SHA-256
↓
DATABASE / SCHEMA / TABLE 权限
↓
SQL 执行及事务提交
任何一层失败,报错都不同。Test-NetConnection=True 只能证明 TCP 端口可达,不代表账户密码或 SQL 权限正确。
7.2 配置监听地址
在 PostgreSQL 管理员的 psql 中:
SHOW listen_addresses;
ALTER SYSTEM SET listen_addresses = '*';
* 允许 PostgreSQL 在可用网卡上监听;企业更建议监听指定私网 IP,将公网直连视为经审批的备选。重启 PostgreSQL 才能应用这项参数。
7.3 生成可用于实验的自签名 TLS 证书
自签名证书仅适合受控实验;生产建议使用组织内部 PKI 或可信 CA,并制定轮换/吊销流程。示例中的域名、IP 均为占位符。绝不能把服务器私钥发给客户端或提交到 Git。
在 Linux root Shell 执行:
# 替换域名和 IP;DNS/IP 必须与客户端最终连接目标一致
openssl req -new -x509 -nodes -days 365 \
-newkey rsa:3072 \
-keyout /var/lib/pgsql/17/data/server.key \
-out /var/lib/pgsql/17/data/server.crt \
-subj '/CN=db.example.internal' \
-addext 'subjectAltName=DNS:db.example.internal,IP:203.0.113.10'
chown postgres:postgres /var/lib/pgsql/17/data/server.key
chown postgres:postgres /var/lib/pgsql/17/data/server.crt
chmod 600 /var/lib/pgsql/17/data/server.key
chmod 644 /var/lib/pgsql/17/data/server.crt
再进入管理员 psql 执行:
ALTER SYSTEM SET ssl = on;
返回 Linux root Shell 后:
systemctl restart postgresql-17
systemctl is-active postgresql-17
su - postgres -c "psql -tAc 'SHOW ssl;'"
期望返回 on。失败时先查看 journalctl -u postgresql-17 -n 60 --no-pager,检查证书文件路径、私钥权限和 postgres 账户读取权限。
7.4 理解 PostgreSQL 的 pg_hba.conf
查看配置位置:
su - postgres -c "psql -tAc 'SHOW hba_file;'"
常见路径:/var/lib/pgsql/17/data/pg_hba.conf。编辑前备份:
cp -a /var/lib/pgsql/17/data/pg_hba.conf \
/var/lib/pgsql/17/data/pg_hba.conf.bak
vi /var/lib/pgsql/17/data/pg_hba.conf
先说明一种与团队权限模型搭配的示例:第八章将创建 app_rw 这样的组角色;可以用 +app_rw 在 HBA 中匹配成员,而不是为每个成员写一条远程认证规则。
# 仅允许受控办公/VPN网段中属于 app_rw 的登录角色通过 TLS 访问开发库
# 198.51.100.0/24 是文档示例;企业必须替换为实际可信 CIDR
hostssl collab_dev +app_rw 198.51.100.0/24 scram-sha-256
# CI/CD 迁移角色只能从 CI 出口连接
hostssl collab_dev svc_migrator 198.51.100.64/32 scram-sha-256
# 应用服务账号只允许实际 VPC 网段
hostssl collab_dev svc_app 10.20.0.0/16 scram-sha-256
重要: 这三条示例需要按真实网络、角色和 CIDR 修改。可以使用每个成员的固定公网
/32,也可以统一 VPN 出口,后者可减轻成员频繁变更 IP 的运维工作。不要把示例网段原样当作真实办公出口。
pg_hba.conf 从上到下使用第一条匹配规则,不会失败后自动尝试下一条。检查文件前面是否存在更宽松的 host ... trust 或其他规则;单纯在文件结尾增加严规则,并不能覆盖前面的宽规则。
加载并验证:
systemctl reload postgresql-17
su - postgres -c "psql -c 'SELECT line_number, type, database, user_name, address, error FROM pg_hba_file_rules ORDER BY line_number;'"
检查 error 列必须为 NULL,并核对规则顺序。如果要开放公网,必须先确认 TLS、HBA 和安全组互相匹配,再让数据库端口可达。
7.5 TLS 的三个层级
| 客户端模式 | 做了什么 | 是否验证服务器身份 |
|---|---|---|
sslmode=disable |
不要求 TLS | 否 |
sslmode=require |
必须加密 | 通常不充分,不能依赖它验证主机身份 |
sslmode=verify-ca |
加密 + 验证可信 CA | 验证 CA,未完整验证主机名 |
sslmode=verify-full |
加密 + CA + 主机名/IP 匹配 | 是,推荐 |
使用自签名实验证书时,可将公开证书 server.crt 安全分发给客户端作为受信任证书(或采用由内部 CA 签发的服务器证书,再分发 CA 根证书),在客户端配置 sslrootcert、verify-full。是否能够严格验证取决于证书链、SAN 和 JDBC 驱动版本;务必实际测试。不要传播 server.key。
查看当前连接是否使用 TLS:
SELECT a.usename, a.datname, s.ssl, s.version, s.cipher
FROM pg_stat_activity AS a
JOIN pg_stat_ssl AS s ON a.pid = s.pid
WHERE a.pid = pg_backend_pid();
7.6 在 Windows 检查网络,读懂报错
Test-NetConnection <ECS_PUBLIC_IP> -Port 5432
| 现象 | 最可能故障层 |
|---|---|
TcpTestSucceeded: False |
IP/端口、安全组、主机防火墙、监听地址 |
no pg_hba.conf entry |
HBA 没有匹配数据库 / 用户 / 源 IP / TLS 方式 |
password authentication failed |
密码不匹配、账号认证问题 |
permission denied for schema app |
Schema 权限不足 |
permission denied for table ... |
表的 DML 权限、所有者或默认权限不匹配 |
certificate verify failed |
CA 不被信任、过期或主机名不匹配 |
常见具体错误:IDEA 将 Database 留成默认的 postgres,而 HBA 只允许 collab_dev,因此即使 Host、用户、密码都正确,仍可能提示 no pg_hba.conf entry ... database "postgres"。应修正客户端连接库名,而不是为了排错放开全部数据库。
八、企业级 PostgreSQL 角色与权限模型:面向任意人数的团队
8.1 为什么不按固定两个人创建“对称管理员账号”?
团队可能有后端、数据工程、测试、AI、运维、外包协作者;成员会入职、离职、轮岗。每新增一个人就复制大量 GRANT 语句,会造成权限漂移,且难以撤销。
可扩展做法:权限给角色组,个人和服务只加入对应角色组。
PostgreSQL Cluster
├── postgres # 超级管理员:只做实例运维
├── svc_migrator (LOGIN) # 数据库迁移:由 CI/CD 持有
├── svc_app (LOGIN) # 应用服务身份,不是员工
├── app_rw (NOLOGIN) # 读写权限集合
├── app_ro (NOLOGIN) # 只读权限集合
├── dev_member_01 (LOGIN) # 示例成员账号
├── dev_member_02 (LOGIN) # 示例成员账号
└── qa_member_01 (LOGIN) # 示例测试账号
Database: collab_dev
├── public # pgvector 扩展等公共对象
└── app # 项目业务 Schema
├── documents
└── ...
dev_member_01 等是匿名编号示例,不对应现实姓名,实际可以由组织的 IAM、员工 ID 映射或自动化入职脚本生成。应用运行身份、迁移身份和人类身份必须分开。
8.2 权限矩阵(最小权限原则)
| 操作 | DBA | 迁移账号 | 应用账号 | 开发者 RW | 测试 RO |
|---|---|---|---|---|---|
| 连接目标库 | √ | √ | √ | √ | √ |
| SELECT / DML | √ | 按迁移需要 | √ | √ | SELECT |
| CREATE / ALTER / DROP 表 | √ | √ | × | × | × |
| 建其他数据库、管理全局角色 | √ | × | × | × | × |
| 更改生产结构 | 审批操作 | CI/CD 执行 | × | × | × |
| 生产账号登录 | 受控 | 受控 | 程序自动 | 默认禁止 | 默认禁止 |
这里的“迁移账号可以执行 DDL”不等于拥有 SUPERUSER 权限。它应该只拥有指定数据库/Schema 的结构管理权限。
8.3 创建组角色、服务角色和示例成员
在 Linux 上以超级管理员进入 psql:
su - postgres
psql
先在 PostgreSQL 实例级别创建角色(Role 在一个 PostgreSQL Cluster 中共享):
-- 权限组:不能直接登录
CREATE ROLE app_rw NOLOGIN;
CREATE ROLE app_ro NOLOGIN;
-- 服务身份:可登录,但不是超级用户
CREATE ROLE svc_migrator LOGIN;
CREATE ROLE svc_app LOGIN;
-- 团队成员:示例编号,可以按需要扩展到任意人数
CREATE ROLE dev_member_01 LOGIN;
CREATE ROLE dev_member_02 LOGIN;
CREATE ROLE qa_member_01 LOGIN;
-- 成员继承对应权限组的权限(PG17 支持指定继承行为)
GRANT app_rw TO dev_member_01 WITH INHERIT TRUE, SET FALSE;
GRANT app_rw TO dev_member_02 WITH INHERIT TRUE, SET FALSE;
GRANT app_ro TO qa_member_01 WITH INHERIT TRUE, SET FALSE;
设置密码不要在公开文章里写真实字符串。 对登录角色逐一执行交互式:
\password svc_migrator
\password svc_app
\password dev_member_01
\password dev_member_02
\password qa_member_01
每条 \password 都会在终端中提示输入两次,不直接出现在可复制的 SQL 语句里。生产建议把服务身份密码放到阿里云 KMS / Secrets Manager 或其他组织认可的密钥管理平台,避免配置文件硬编码。
8.4 建立业务 Schema、连接授权和历史对象授权
切换到项目库:
\c collab_dev
在新建的、可清理的示例库中执行:
-- 不让所有角色默认具备连接该库的权利
REVOKE ALL ON DATABASE collab_dev FROM PUBLIC;
-- 谁可以连接数据库
GRANT CONNECT ON DATABASE collab_dev
TO app_rw, app_ro, svc_migrator, svc_app;
-- 将业务 DDL 归于一个专门的迁移主体;业务应用不拥有 Schema
CREATE SCHEMA app AUTHORIZATION svc_migrator;
-- 仅允许组角色使用 Schema,不授予普通开发者 CREATE
GRANT USAGE ON SCHEMA app TO app_rw, app_ro, svc_app;
-- 现有表(如果以后已存在)授权
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app
TO app_rw, svc_app;
GRANT SELECT ON ALL TABLES IN SCHEMA app TO app_ro;
-- 如果表使用序列/serial,也需要相应权限
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA app
TO app_rw, svc_app;
-- 注意:由迁移账号创建的未来对象,要预设默认权限
ALTER DEFAULT PRIVILEGES FOR ROLE svc_migrator IN SCHEMA app
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_rw, svc_app;
ALTER DEFAULT PRIVILEGES FOR ROLE svc_migrator IN SCHEMA app
GRANT SELECT ON TABLES TO app_ro;
ALTER DEFAULT PRIVILEGES FOR ROLE svc_migrator IN SCHEMA app
GRANT USAGE, SELECT ON SEQUENCES TO app_rw, svc_app;
-- 收紧 public schema 的 DDL;Pg15+ 新库常已默认如此
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
这套设计具有可扩展性:新增成员只需加入 app_rw 或 app_ro;新增表由 svc_migrator 创建后,默认权限生效。
重要细节:ALTER DEFAULT PRIVILEGES 仅影响之后创建的对象,且基于实际创建该对象的角色。如果 DBA 用 postgres 直接创建了某张新表,就不会自动应用 FOR ROLE svc_migrator 的默认授权;应坚持一个迁移执行身份。默认权限不能修复历史表,历史表须使用 GRANT ... ON ALL TABLES 显式补齐。参见 PostgreSQL 官方文档。
8.5 让 pgvector 与独立业务 Schema 协同工作
前面为了方便把 vector 扩展创建在 public。业务表统一创建在 app,可以显式写 public.vector(1536),也可以对符合权限要求的账户设置 search_path:
ALTER ROLE dev_member_01 IN DATABASE collab_dev
SET search_path TO app, public;
ALTER ROLE dev_member_02 IN DATABASE collab_dev
SET search_path TO app, public;
ALTER ROLE svc_app IN DATABASE collab_dev
SET search_path TO app, public;
ALTER ROLE svc_migrator IN DATABASE collab_dev
SET search_path TO app, public;
1536 只是某类 Embedding 维度的示例,必须根据实际模型输出维度修改。更重要的是不允许不可信角色在应用默认 search_path 中的 Schema 创建对象,以降低对象名称劫持风险。
8.6 让迁移账号真正能执行迁移
迁移角色 svc_migrator 是 app Schema 所有者,但还必须具备该数据库的 CONNECT 权限(前面的示例已授予)。此后迁移任务使用该身份连接 collab_dev,在 app 下建立业务表。
-- 用迁移身份连接后执行(而不是用 postgres DBA 手动执行):
CREATE TABLE app.documents (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
title TEXT NOT NULL,
content TEXT NOT NULL,
embedding public.vector(3),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
该表会由迁移身份持有,app_rw、app_ro、svc_app 获得前文设定的默认权限。生产应把上述建表 SQL 放入 Flyway 迁移文件,不能靠人在 IDEA 手工执行。
8.7 团队扩容、转岗、离职时怎么处理?
新成员加入:
-- 管理员批准并配置登录凭据后
CREATE ROLE dev_member_03 LOGIN;
GRANT app_rw TO dev_member_03 WITH INHERIT TRUE, SET FALSE;
GRANT CONNECT ON DATABASE collab_dev TO app_rw; -- 如已存在,无需重复
转为只读:
REVOKE app_rw FROM dev_member_03;
GRANT app_ro TO dev_member_03 WITH INHERIT TRUE, SET FALSE;
紧急禁用(例如离职):
ALTER ROLE dev_member_03 NOLOGIN;
REVOKE app_rw FROM dev_member_03;
REVOKE app_ro FROM dev_member_03;
NOLOGIN 主要阻止新连接;已有活动会话需要由管理员按需终止并撤销网络/VPN/系统访问,相关 Token 与凭据也应失效。确认对象所有权、依赖和审计要求后,再考虑彻底删除数据库角色。生产环境应实现可审计的入职/离职自动化。
8.8 什么时候需要更多隔离?
- 测试团队:只读或独立 QA 数据库,不随意更改共享 DEV 数据。
- 外部合作方:单独隔离测试环境、IP/身份/审计;最小样例数据。
- 多租户 SaaS:数据库账号不能代替租户级授权;应在应用层校验租户,必要时结合 PostgreSQL RLS。
- AI Agent 服务:工具调用执行 SQL 时必须限制可访问表/操作,不允许 LLM 把管理员数据库账号当成通用工具。
- 生产 CI/CD:单独迁移角色、独立审批环境、独立密钥和审计链路。
九、团队到底怎么协作:从 Issue 到上线的可执行流程
9.1 为每一种环境定义独立边界
| 环境 | 目标 | 数据来源 | 谁能改表结构 | 典型约束 |
|---|---|---|---|---|
| LOCAL | 独立开发和调试 | 本地造数/脱敏数据 | 开发者通过迁移测试 | 可重建 |
| CI/EPHEMERAL | 每个 PR 自动验证 | 自动初始化 | CI 迁移任务 | 用后即销毁 |
| DEV | 跨模块共享联调 | 受控共享测试数据 | 合并后的迁移流水线 | 禁止手工漂移 |
| TEST/QA | 测试与回归 | 固定测试集 | 测试发布流水线 | 变更可追溯 |
| STAGING | 生产近似演练 | 脱敏数据/模拟数据 | 发布流水线 | 尽量同生产版本 |
| PROD | 真实业务 | 真实数据 | 经审批的发布流水线 | 最小权限、备份、监控 |
这比“所有人连一台公网 ECS,共用一个库”更符合企业真实研发。共享 DEV 有价值,但不应替代 LOCAL、CI、TEST、STAGING、PROD 的独立边界。
9.2 Git 仓库建议结构
project/
├── frontend/ # Web 前端
├── backend/ # Java Spring Boot
│ ├── src/main/java/...
│ └── src/main/resources/
│ └── db/migration/ # Flyway 版本化 SQL
│ ├── V1__init_schema.sql
│ ├── V2__create_documents.sql
│ └── V3__add_document_status.sql
├── ai-service/ # Python Agent / RAG
├── infra/
│ ├── docs/ # 安装手册、网络图、值班手册
│ ├── db/ # 权限模板、初始化文档、备份演练
│ └── ci/ # 部署脚本/流水线定义
├── .env.example # 只放变量名称和假的示例值
├── .gitignore # 忽略真实 .env、密钥、备份
└── README.md
单一事实来源(Single Source of Truth):每个应用所用 Schema 的正式迁移版本必须有唯一权威目录;跨多个服务时约定“哪一个服务/团队拥有哪部分 Schema”,避免各自另起一套未经协商的迁移脚本。
9.3 一个真实需求怎样推进?
以“文档增加状态字段”为例:
- 提出 Issue:说明字段用途、取值、API 兼容、现有数据的处理方式。
- 建分支:
feature/document-status;本地数据库通过迁移达到基线版本。 - 编写迁移:新增
V3__add_document_status.sql,不要修改已上线的V1/V2历史文件。 - 提交业务代码:Java DTO/Entity/Mapper、Python 查询逻辑、前端字段显示一并修改。
- 跑测试:老数据是否能查、默认值是否合理、迁移可否从空库和已有库顺利执行。
- 发 PR:审查 SQL、索引、锁、兼容窗口、数据回填规模与回滚/前向修复方案。
- CI 校验:临时 DB 运行全部迁移,再跑单测/集成测试。
- 合并主干:DEV 流水线执行迁移,团队进行联调;然后 TEST、STAGING、PROD 按发布策略推进。
数据库结构变更不是“在一个人的 IDEA 里改好,然后发截图给同事”。 它是代码变更的一部分,应能重复执行、审查、审计。
9.4 Flyway 实战:版本化 Schema
Flyway 会维护 Schema History 表,记录迁移版本、校验信息和执行结果。
文件命名示例:
V1__create_documents.sql
V2__add_search_index.sql
V3__add_status_column.sql
R__refresh_views.sql # 可重复迁移,需理解重跑语义
V1 示例:由 svc_migrator 在 app Schema 运行(本节以一个全新数据库为例,不要和第 8.6 节同名建表 Demo 重复执行)。
CREATE TABLE app.documents (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
title VARCHAR(200) NOT NULL,
body TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
V2 示例:
CREATE INDEX idx_documents_created_at
ON app.documents(created_at DESC);
V3 示例:
ALTER TABLE app.documents
ADD COLUMN status VARCHAR(20) DEFAULT 'draft';
注意:这里刻意把 status 初始设置成允许 NULL 或带默认值的兼容变更。对真实大表、非空约束、索引创建、数据回填,要另行做锁与性能分析;例如 PostgreSQL 的 CREATE INDEX CONCURRENTLY 不能直接放入普通事务块,必须按 Flyway 的相应迁移执行方式处理。
9.5 为什么“Expand → Migrate → Contract”很重要?
企业系统有多个后端实例、老版本客户端和持续运行任务,一次性“删旧列、加新列”很可能让旧代码立刻报错。
Expand → 先增加新列/新表,兼容旧代码
Migrate → 双写或分批迁移历史数据,验证读写一致
Contract → 所有实例升级并确认无旧依赖后,再删除旧列/旧逻辑
例子:原来只有 username,新版本想拆成 given_name、family_name。不应在同一瞬间删除 username,应按兼容窗口分阶段发布。
9.6 每个 PR 的数据库测试策略
建议至少覆盖四种场景:
| 测试 | 必须验证什么 |
|---|---|
| Fresh DB | 空数据库从 V1 一直迁移到最新是否成功 |
| Upgrade DB | 有旧版本表和样本数据时能否升级 |
| Cross-service | Java / Python 服务是否使用兼容的 Schema / 数据类型 |
| Permission test | svc_app能 DML,但不能随意ALTER TABLE |
如果团队使用 Docker/Testcontainers,可在 CI 为每个 PR 启动临时 PostgreSQL(含 pgvector)或 MySQL,而不必污染共享 DEV。生产部署不使用 Docker,也不影响 CI 使用临时容器进行数据库测试。
9.7 CI/CD 的推荐阶段
CI 流水线
① Lint / SAST / Secrets scan
② Build / Unit Tests
③ 启动隔离测试数据库
④ 执行 Flyway migrate
⑤ 数据库集成测试
⑥ 静态检查 SQL 与权限
⑦ 构建产物
CD 流水线
① 选择 DEV/TEST/STAGING/PROD
② 校验目标环境、版本与审批
③ 备份/快照与恢复预案
④ 使用专门迁移凭据执行 migrate
⑤ 验证迁移版本
⑥ 发布应用(使用单独运行时凭据)
⑦ 健康检查 / 指标观察 / 按策略回退应用
凭据分离是重点:
svc_migrator:仅在迁移 Job/流水线阶段使用。svc_app:Spring Boot、Python 应用的普通运行时连接。dev_member_xx:人的 IDEA / DBeaver 调试账号,不应被生产应用使用。
生产数据库的迁移不能仅靠在代码中自动 ddl-auto=update;Java JPA 可以考虑 ddl-auto=validate,只验证实体与数据库结构是否匹配。
9.8 数据冲突:多人同时测试到底怎么做?
同一个共享开发库内,不同开发者可能相互覆盖数据、改变状态或触发定时任务。因此建议:
- 为测试记录设置隔离键,如
test_run_id或scenario_id,让自动化测试只清理自己创建的数据。 - 使用幂等 Seed 数据脚本:重复运行不会无限插入重复记录。
- 测试账号、模拟租户与真实账号完全隔离。
- 大型集成测试优先使用独立测试库或 CI 临时数据库。
- 关键业务状态与并发场景使用事务、唯一约束、乐观锁/悲观锁等机制,不能靠团队约定“不同时点按钮”。
- 不要直接将生产个人信息复制到测试库;必须进行脱敏、数据最小化与权限控制。
9.9 一个成员如何完成入职开发(Onboarding)?
拿到仓库只读/写入权限
↓
配置本地 JDK / Python / IDEA / Git
↓
按 README 下载示例 .env.example
↓
申请个人数据库账号(不是应用账号)
↓
授予 app_rw 或 app_ro 权限组
↓
通过 VPN / 堡垒机 / 已批准 IP 连接 DEV
↓
执行 SELECT current_user / current_database
↓
本地执行迁移验证 + 单测
↓
完成第一个 PR
Offboarding 反向执行:Git/云平台/VPN/数据库/监控等权限同时收回,轮换相关共享服务凭据,留存必要审计日志。
十、IDEA、DBeaver、Java 与 Python 怎样连接数据库
10.1 IDEA Database 窗口
打开 IDEA:View → Tool Windows → Database,新增 PostgreSQL 数据源,填入:
| 字段 | 示例值 | 解释 |
|---|---|---|
| Host | <DB_HOST> |
ECS 私网 IP / 受控公网 IP / 托管数据库地址 |
| Port | 5432 |
PostgreSQL 端口 |
| Database | collab_dev |
项目库名,不能误填postgres |
| User | dev_member_01 |
单独分配给该成员的账号 |
| Password | 从安全渠道获得 | 不写入公开教程 |
| SSL | verify-full(推荐) |
配置 CA/证书与主机名验证 |
点击 Test Connection,成功后打开 SQL Console:
SELECT current_database(), current_user, version();
SHOW search_path;
常用操作:
SELECT * FROM app.documents LIMIT 20;
SELECT COUNT(*) FROM app.documents;
IDEA 结果表格支持浏览和编辑,但团队表结构应由 Flyway 负责。如果使用手动事务提交模式,未提交的 INSERT/UPDATE 对其他会话通常不可见。
10.2 用数据库实际验证权限
成员账号:
SELECT current_user;
SELECT has_schema_privilege(current_user, 'app', 'USAGE');
SELECT has_schema_privilege(current_user, 'app', 'CREATE');
在上文最小权限模型中,普通开发账号预期能使用 Schema,不能直接在共享 app Schema 随意创建结构。是否有表 DML 权限要对具体目标表继续验证:
SELECT has_table_privilege(current_user, 'app.documents', 'SELECT');
SELECT has_table_privilege(current_user, 'app.documents', 'INSERT');
10.3 Spring Boot 示例(Java)
环境变量由本地 .env 工具、操作系统或部署平台注入,不提交真实口令:
spring:
datasource:
url: jdbc:postgresql://${DB_HOST}:${DB_PORT:5432}/${DB_NAME:collab_dev}?sslmode=verify-full
username: ${DB_APP_USER}
password: ${DB_APP_PASSWORD}
hikari:
maximum-pool-size: 5 # 小规格示例,按总实例数/并发调整
jpa:
hibernate:
ddl-auto: validate # 不在共享/生产环境自动更新表结构
注意:JDBC 驱动的证书信任配置(例如 root certificate 路径)需按驱动版本和运行环境补齐。不能把 sslmode=verify-full 单独当作完成 TLS 配置的全部工作。
10.4 Python 示例(psycopg 3)
import os
import psycopg
with psycopg.connect(
host=os.environ["DB_HOST"],
port=int(os.getenv("DB_PORT", "5432")),
dbname=os.getenv("DB_NAME", "collab_dev"),
user=os.environ["DB_APP_USER"],
password=os.environ["DB_APP_PASSWORD"],
sslmode="verify-full",
sslrootcert=os.environ["DB_SSL_ROOT_CERT"],
) as conn:
with conn.cursor() as cur:
cur.execute("SELECT current_database(), current_user")
print(cur.fetchone())
如果数据库位于可信私网且暂时没有可验证证书,可以在受控实验环境中先使用 sslmode=require 验证网络,但这不等于已经完成生产要求的服务器身份验证。
10.5 Redis 连接示例
Java 可使用 Spring Data Redis;Python 常用 redis-py。Redis 的连接地址默认应为 127.0.0.1、VPC 私网地址或通过隧道映射的本地地址;别把公网示例变成 redis://<PUBLIC_IP>:6379。
应用应设置连接超时、异常降级、TTL、键前缀及观察指标。数据库连接池和 Redis 客户端连接数需要基于所有应用实例的总和规划。
十一、资源管理与基础运维:2 GiB 演示机和生产集群的区别
11.1 演示机的优先级
- 默认 PostgreSQL + Redis 运行;暂时不用 MySQL 时关闭它。
- 控制连接数及应用连接池:例如三个应用实例各开 20 个连接,数据库并不是“只有一个应用在访问”。
- 观察操作系统 OOM、Swap、磁盘和 I/O,尤其是向量索引建立和批量导入时。
- 数据盘快满、磁盘延迟严重、内存持续吃紧时,及时扩容或拆分服务,不要只反复重启数据库。
11.2 PostgreSQL 调参仅给起点,不给“万能最优配置”
先查看:
SHOW shared_buffers;
SHOW work_mem;
SHOW maintenance_work_mem;
SHOW max_connections;
SHOW effective_cache_size;
2 GiB 演示机可根据实际负载试验较保守参数,例如 shared_buffers=256MB、work_mem=4MB、maintenance_work_mem=64MB、较低 max_connections。这些不是生产通用数值;work_mem 可能按排序/Hash 操作和并发放大,effective_cache_size 只是规划器估算值,不是内存硬限制。
想查看当前进程:
free -h
df -h
ps -eo pid,user,rss,%mem,cmd --sort=-rss | head -20
journalctl -u postgresql-17 -n 60 --no-pager
journalctl -u redis -n 60 --no-pager
PostgreSQL SQL 侧:
SELECT datname, numbackends, xact_commit, xact_rollback
FROM pg_stat_database
ORDER BY datname;
SELECT state, COUNT(*)
FROM pg_stat_activity
GROUP BY state;
11.3 数据库的监控指标
| 层级 | 建议观察 |
|---|---|
| ECS | CPU、RAM、Swap、磁盘容量、IOPS、网络带宽 |
| PostgreSQL | 活跃连接、锁等待、慢 SQL、长事务、复制延迟、缓存命中 |
| MySQL | 连接数、慢查询、InnoDB 指标、复制状态 |
| Redis | used_memory、maxmemory、evicted_keys、命中率、延迟 |
| 应用 | API 错误率、延迟 P95/P99、连接池等待、事务失败 |
| 发布 | 迁移版本、失败发布、备份恢复验证、配置漂移 |
企业可接入云监控、Prometheus/Grafana、集中日志及告警;关键是明确阈值、值班人员与处理 Runbook,而不是只安装一个监控面板。
十二、数据库备份、恢复、升级和灾难演练
12.1 备份的核心不在“有文件”,而在“能恢复”
备份体系应回答:
- 多长时间备份一次?保留多久?
- 数据库误删后最多能容忍丢失多少数据(RPO)?
- 故障后多久必须恢复服务(RTO)?
- 备份是否加密、异地保存,并与生产账户隔离?
- 最近一次成功的恢复演练是什么时候?
12.2 PostgreSQL:逻辑备份与恢复演练
在 Linux root Shell:
install -d -m 700 -o postgres -g postgres /var/backups/postgresql
# Linux 上的 postgres 用户执行,输出自定义归档格式
su - postgres -c \
'pg_dump -Fc -d collab_dev -f /var/backups/postgresql/collab_dev.dump'
ls -lh /var/backups/postgresql/collab_dev.dump
恢复必须使用另外一个演练数据库,避免覆盖线上业务:
su - postgres -c 'createdb collab_restore'
su - postgres -c \
'pg_restore --no-owner --no-acl -d collab_restore /var/backups/postgresql/collab_dev.dump'
恢复成功后:
su - postgres -c \
"psql -d collab_restore -c 'SELECT COUNT(*) FROM public.vector_demo;'"
上面的 vector_demo 只在前面确实创建了该教学表时存在。通用恢复验证应检查实际业务表、约束、行数以及应用查询。pgvector 扩展在恢复目标环境必须可安装、可用;对某些备份的恢复顺序和角色依赖需要预先演练。
pg_dump 备份单库,并不包括所有实例级角色等全局对象。要恢复完整实例权限体系,还应结合 pg_dumpall --globals-only、运维脚本/基础设施声明或托管数据库的自动备份方案。生产如要求更小 RPO,应考虑物理备份 + WAL 归档/PITR,单次 pg_dump 无法代替连续恢复能力。
12.3 MySQL 逻辑备份
如 MySQL 在使用中:
mysqldump -u root -p \
--single-transaction --routines --triggers \
collab_dev > collab_dev.sql
这是适合相应 InnoDB 场景的逻辑导出示例,并非所有引擎、规模或权限配置都通用。大库或生产需要评估一致性、锁、备份窗口、Binlog、PITR 和托管备份能力。
12.4 Redis 备份按数据职责选
仅承担缓存:可以允许重建,重点测试 Redis 故障时应用是否降级;若承担不能丢失的状态,则必须明确定义 AOF/RDB、故障切换、持久化和恢复流程。不要套用“Redis 是缓存,所以永远不用备份”的绝对说法。
12.5 升级前与升级后
数据库小版本补丁升级和主版本升级风险不同。主版本升级需明确 pgvector 插件兼容、驱动、DDL、扩展、数据格式、停机/切换方式及回滚策略。先在测试/预发布环境重放升级,不要在生产节点直接“dnf update 全装了再看”。
十三、常见报错总览:按故障层次处理
| 报错/现象 | 原因优先级 | 推荐动作 |
|---|---|---|
SSHConnection timed out |
TCP 22 不通 | 公网地址、安全组、ssh 服务、防火墙 |
Unit mysqld.service does not exist |
只添加仓库、未安装服务端 | 检查rpm -q mysql-community-server |
MySQLERROR 1045 |
错密码 / Host 不匹配 | 区分 Linux root 与 MySQL root、核对账号来源 |
MySQLERROR 1819 |
密码强度不足 | 使用满足策略的强密码 |
PGDG 仓库rhel-3404 |
EL 仓库识别 Alibaba Cloud Linux 3 版本错误 | 只针对兼容 PGDG 仓库修正 releasever |
postgresql-17.service not found |
未正确安装 PG17 或 unit 名称不同 | rpm -q、systemctl list-unit-files |
psql: syntax error near "su" |
在 SQL Console 里输入 Linux 命令 | \q返回 Shell 再执行 |
pgvector extension is not available |
扩展包/PG 大版本不匹配 | 核对pgvector_17、PG17、extension 文件 |
SHOW ssl = off |
TLS 未开启 | 检查证书、ALTER SYSTEM、服务重启 |
5432 端口False |
网络层未通 | ss、安全组、系统防火墙、监听 |
no pg_hba.conf entry ... postgres |
客户端数据库名错误或 HBA 不匹配 | 核对Database=collab_dev、IP、用户、TLS |
permission denied for schema |
没有 USAGE/CREATE | 检查 Schema 授权和当前用户 |
| 能查询但不能建表 | 角色只允许 DML | 这是设计预期,DDL 走迁移流水线 |
| 新表无法被应用查询 | 默认权限绑定的创建者不对 | 检查FOR ROLE svc_migrator与实际 owner |
RedisPONG但重启配置丢失 |
CONFIG SET 仅修改运行时 | 检查 redis.conf 或 CONFIG REWRITE 是否成功 |
| 2 GiB ECS OOM | 并发/数据库/缓存超出内存 | 查 OOM、连接池、批处理,扩容或拆分 |
快速排错工具箱:
systemctl status postgresql-17 --no-pager
systemctl status mysqld --no-pager
systemctl status redis --no-pager
ss -lntp | grep -E ':(22|3306|5432|6379)\b'
free -h
df -h
journalctl -u postgresql-17 -n 80 --no-pager
Test-NetConnection <DB_HOST> -Port 5432
Test-NetConnection <DB_HOST> -Port 3306
-- PostgreSQL 管理员查询
SELECT line_number, type, database, user_name, address, error
FROM pg_hba_file_rules
ORDER BY line_number;
注意:pg_hba_file_rules.error IS NULL 只能说明解析未发现错误,不保证认证逻辑和网络边界符合预期,还要实际从各类客户端验证。
十四、不同企业场景下,怎么调整这套范式?
14.1 场景 A:个人学习 / 大学课程 / 演示项目
- 一台轻量 ECS,安装一种主数据库 + Redis(按需)。
- 开发者本地跑应用,数据库服务器主要提供持久化与受控访问。
- 需要公网连接时,优先限定本机公网
/32、开启 TLS,不把 Redis 公网开放。 - 可以手动做 SQL 实验,但保留迁移文件习惯。
- 每日基础备份,周度恢复演练。
14.2 场景 B:5~15 人的 Java + Python + AI 团队
- PostgreSQL + pgvector 作为统一业务/向量存储,Redis 缓存。
- 个人开发使用本地/临时数据库;共享 DEV 专供跨模块联调。
- 按团队角色授权:
app_rw、app_ro;项目应用与迁移使用不同身份。 - Git PR + Flyway + CI 数据库测试成为强制门禁。
- 应用开发与数据库容量规划分离,尽量使用 VPC 私网或 RDS。
- 对外合作使用隔离测试数据集,不共享生产数据。
14.3 场景 C:多团队、多微服务、大型平台
- 根据数据边界明确服务拥有者:例如用户服务管理用户数据,文档服务管理文档数据。
- 不应让所有微服务无限制地共享所有业务表;可以采用服务独立数据库、独立 Schema 或明确共享契约。
- 共享数据通过 API、事件消息、只读数据产品或经设计的同步机制提供。
- schema migration 由各服务所属团队维护,并有跨服务兼容测试。
- 引入集中身份、数据库审计、指标告警、容量管理、高可用与灾难恢复。
14.4 场景 D:多租户 SaaS + Agent / RAG 平台
- 每条业务记录明确 tenant 范围;所有查询都做租户鉴权。
- 向量检索必须与租户过滤结合,不能只按全局向量相似度返回结果。
- Agent 工具的 SQL 权限应缩到最低,不把 LLM 生成的 SQL 当可信命令直接执行。
- 凭据与用户上传文档需要隔离、加密、审计与生命周期清理。
- 向量生成涉及模型版本、重嵌入、索引重建和语义回归测试。
- 数据合规、保留周期、删除请求与组织级安全策略优先于“功能做出来就行”。
14.5 场景 E:仅 MySQL 项目,不做向量检索
完全没有必要因为“AI 热门”就把成熟 MySQL 业务强行迁移到 PostgreSQL。可以继续 MySQL + Redis,使用外部向量服务、独立检索引擎或适合自身需求的 AI 模块;需要评估的是实际约束和总拥有成本,而不是技术名字的新旧。
十五、部署与企业协作最终验收清单
15.1 基础设施验收
- [ ] ECS 实例、系统版本、架构、磁盘与内存已核实
- [ ] SSH 密钥/账号按人分配,管理权限可追溯
- [ ] 安全组只保留必要端口和受控来源
- [ ] Linux 防火墙与数据库监听地址一致
- [ ] 数据盘、日志、备份容量与保留期已规划
- [ ] systemd 启停、开机自启、日志查看正常
15.2 数据服务验收
- [ ] MySQL 8.4 服务/账号/权限/TLS(若启用 MySQL)
- [ ] Redis PING 正常、内存策略与持久化策略符合用途
- [ ] Redis 6379 未向不可信公网开放
- [ ] PostgreSQL 17 初始化完成、服务正常
- [ ] pgvector 扩展在目标数据库启用,并通过向量查询
- [ ] 账号不是超级管理员,业务连接遵循最小权限
- [ ] 客户端 TLS 身份验证经实际连接验证
15.3 团队治理验收
- [ ] LOCAL、CI、DEV、TEST、STAGING、PROD 边界明确
- [ ] 每名成员独立身份,加入相应权限组
- [ ] 应用运行账号与迁移账号分离
- [ ] DDL 只通过经过评审的迁移流程进入共享环境
- [ ] Git 仓库存储版本化 SQL,CI 测试空库和升级路径
- [ ] 已定义共享测试数据的隔离/清理/脱敏机制
- [ ] 已有 Onboarding / Offboarding / 密码轮换流程
- [ ] 备份有实际恢复演练,已确定 RPO / RTO
- [ ] 监控、日志、告警和事故 Runbook 可用
最低合格标准不是“IDEA 变绿、能连上数据库”,而是:新成员可以按文档独立接入;新变更可以经评审自动迁移;服务故障时能够定位;数据可以恢复;权限可以随时撤销。
十六、全篇总结:从“会安装数据库”进化到“会交付数据基础设施”
最初我们学习的是:通过 FinalShell 登录 Linux、dnf install、systemctl start,让 MySQL、PostgreSQL 和 Redis 启动。
再往上是理解:软件仓库、进程、端口、VPC、安全组、TLS、数据库身份、Schema 和事务。
真正到了团队项目,还要继续回答:
- 谁可以访问什么数据? —— 权限、身份、环境隔离。
- 谁可以改表结构? —— Schema 所有权、迁移账号、PR 审查。
- 不同版本如何共存? —— 向前兼容、Expand/Migrate/Contract。
- 多人开发怎样互不干扰? —— 本地隔离、CI 临时库、共享 DEV 约束。
- 出错如何恢复? —— 日志、监控、备份、恢复演练。
- 系统扩容怎么办? —— 托管服务、连接池、容量与高可用规划。
一句话总结:安装数据库解决“今天能运行”,规范化数据基础设施解决“一个不断变化的团队,明天仍然能稳定交付”。
十七、官方参考资料(建议持续核对)
- PostgreSQL 官方文档(17):https://www.postgresql.org/docs/17/
- PostgreSQL 认证文件
pg_hba.conf:https://www.postgresql.org/docs/17/auth-pg-hba-conf.html - PostgreSQL Schema 与安全:https://www.postgresql.org/docs/17/ddl-schemas.html
- PostgreSQL 角色与权限:https://www.postgresql.org/docs/17/sql-createrole.html
- PostgreSQL 默认权限:https://www.postgresql.org/docs/17/sql-alterdefaultprivileges.html
- PostgreSQL 客户端连接与 TLS:https://www.postgresql.org/docs/17/libpq-connect.html
- PostgreSQL 防止假冒服务器:https://www.postgresql.org/docs/17/preventing-server-spoofing.html
- PostgreSQL RPM/Yum 安装:https://www.postgresql.org/download/linux/redhat/
- pgvector 官方仓库:https://github.com/pgvector/pgvector
- MySQL 8.4 官方手册:https://dev.mysql.com/doc/refman/8.4/en/
- MySQL 加密连接:https://dev.mysql.com/doc/refman/8.4/en/using-encrypted-connections.html
- MySQL Yum 仓库:https://dev.mysql.com/downloads/repo/yum/
- Redis 安全:https://redis.io/docs/latest/operate/oss_and_stack/management/security/
- Redis 配置:https://redis.io/docs/latest/operate/oss_and_stack/management/config/
- Redis 内存优化:https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/memory-optimization/
- Flyway 项目及迁移参考:https://github.com/flyway/flyway
- 阿里云 ECS 产品文档:https://help.aliyun.com/zh/ecs/
复现原则:本文故意保留了 Linux 发行版兼容性、数据库安全、版本化迁移以及团队治理中的关键限制。不要因为某条示例命令在一台实验机执行成功,就跳过生产环境的变更审批、备份、回滚预案与验证。