- 使用 pgx/sqlx 建立连接,从环境变量读取数据库密码 - 添加本地 PostgreSQL Compose 配置与初始迁移 - 补充 Goose 操作手册和连接串排错说明
180 lines
7.3 KiB
Markdown
180 lines
7.3 KiB
Markdown
# Goose 迁移操作手册
|
||
|
||
Lyra 数据库迁移的**唯一完整操作手册**。`migrations/` 目录是数据库结构的
|
||
唯一正式执行来源;[demo.sql](demo.sql) 仅保留作参考,不要再手工执行它。
|
||
迁移目录本身的说明见 [migrations/README.md](../migrations/README.md)。
|
||
|
||
## 目录与前提
|
||
|
||
- 所有 goose 命令都在 Lyra module 根目录(`migrations/` 的上一级)执行:
|
||
|
||
```bash
|
||
cd go/projects/lyra # 从仓库根出发
|
||
```
|
||
|
||
- 目标数据库**已创建**:迁移不负责建库,`lab` 库需先存在。
|
||
- 首次迁移要求目标对象**尚不存在**:如果之前手工执行过 demo.sql 建过
|
||
同名对象,`up` 会报 `already exists`,先按故障表评估处理。
|
||
|
||
## 安装 goose CLI(固定 v3.29.0)
|
||
|
||
goose 只作为独立命令行工具使用(Go 应用尚未引入 goose 依赖):
|
||
|
||
```bash
|
||
go install -tags='no_clickhouse no_libsql no_mssql no_mysql no_sqlite3 no_vertica no_ydb' github.com/pressly/goose/v3/cmd/goose@v3.29.0
|
||
goose -version # 应输出 v3.29.0
|
||
```
|
||
|
||
- 固定 `@v3.29.0`,保证各机器行为一致。
|
||
- `-tags=...` 是可选的 PostgreSQL 专用精简构建(`no_*` 标签已在 goose
|
||
v3.29.0 源码中核实);不追求精简可省略。
|
||
- 二进制装到 `$(go env GOPATH)/bin`(设了 `GOBIN` 则装到 `GOBIN`)。
|
||
`goose -version` 提示找不到命令时,把该目录加入 `PATH`:
|
||
|
||
```bash
|
||
export PATH="$PATH:$(go env GOPATH)/bin" # 可写入 ~/.bashrc
|
||
```
|
||
|
||
## 配置连接:.env.local
|
||
|
||
goose **不读取**应用的 `etc/lyra.toml`,也不会只凭 `PG_PASSWORD` 之类的
|
||
应用环境变量自动拼出连接串——它只认自己环境变量里的 `GOOSE_DBSTRING`。
|
||
在 module 根创建 `.env.local` 并追加以下内容(**全部是占位值**,换成实际
|
||
信息;`lab` 库需已创建):
|
||
|
||
```bash
|
||
GOOSE_DRIVER=postgres
|
||
GOOSE_MIGRATION_DIR=./migrations
|
||
GOOSE_DBSTRING="postgres://example_user:URL_ENCODED_PASSWORD@127.0.0.1:5432/lab?sslmode=disable"
|
||
```
|
||
|
||
要点:
|
||
|
||
- `GOOSE_DRIVER=postgres` 是 **goose 的驱动名**;Go 应用内用的是 pgx
|
||
(驱动名 `pgx`),两边名字不同但都连 PostgreSQL,不要混填。
|
||
- 本仓库 `.gitignore` 已忽略 `.env*`,`.env.local` 不会提交;建议再执行
|
||
`chmod 600 .env.local` 收紧文件权限。
|
||
- `127.0.0.1` 与端口必须换成**实际可达**的地址和端口(远程/容器按实际
|
||
映射填写)。
|
||
- `sslmode=disable` 仅适用于本地确认无 TLS 要求的场景;远程环境按部署的
|
||
TLS 要求调整该参数。
|
||
- 本手册所有 goose 命令统一用 `-env=.env.local` 显式加载该文件:goose
|
||
默认读的是 `.env`,`-env=.env.local` 既明确来源、又避免误用其他本地
|
||
配置。连接信息配置一次即可,无需每次交互输入密码。
|
||
- 系统环境里已有的同名变量(如曾 `export` 过 `GOOSE_DBSTRING`)可能优先
|
||
于 `.env.local` 生效:改了 `.env.local` 仍行为异常时,先查是否存在同名
|
||
环境变量——只列变量名、不打印值,避免泄露连接串:
|
||
|
||
```bash
|
||
env | cut -d= -f1 | grep -i '^GOOSE'
|
||
```
|
||
|
||
## 连接串特殊字符:URL 编码
|
||
|
||
`GOOSE_DBSTRING` 是 URL 格式:**用户名、密码**中的特殊字符必须逐个做
|
||
百分号编码,否则主机名/路径解析会错位。
|
||
|
||
- 对用户名和密码分别编码,**不要把整串 URL 一起编码**,也不要为迁就 DSN
|
||
去修改数据库里的实际密码。避免对已经编码的内容重复编码。
|
||
- 不要把真实密码交给在线网站转换;按下表手工替换即可。
|
||
|
||
| 字符 | 编码 | 字符 | 编码 |
|
||
|---|---|---|---|
|
||
| `@` | `%40` | `:` | `%3A` |
|
||
| `#` | `%23` | `/` | `%2F` |
|
||
| `%` | `%25` | `?` | `%3F` |
|
||
|
||
示例:密码 `demo#pass`(演示值)在 DSN 中写成 `demo%23pass`。
|
||
|
||
## 常用命令
|
||
|
||
```bash
|
||
# 离线检查迁移文件能否被 goose 解析(注解与语句拆分);
|
||
# 不连接数据库,也不验证 SQL 在 PostgreSQL 上的语义
|
||
goose -env=.env.local validate
|
||
|
||
# goose 工具自身版本(与数据库里的迁移版本是两回事)
|
||
goose -version
|
||
|
||
# 连接数据库:查看迁移状态 / 当前版本
|
||
goose -env=.env.local status
|
||
goose -env=.env.local version
|
||
|
||
# 执行迁移(连接数据库)
|
||
goose -env=.env.local up
|
||
```
|
||
|
||
注意:v3.29.0 的 `validate` **不接受位置路径参数**,迁移目录只由
|
||
`GOOSE_MIGRATION_DIR`(`.env.local` 已设 `./migrations`)或 `-dir` 标志
|
||
决定。`validate` 通过只证明文件能解析,不证明连接正常或 SQL 语义正确。
|
||
|
||
## 首次初始化
|
||
|
||
前提:`lab` 库已创建,且 00001 中的对象(函数、三张表、索引、触发器)
|
||
都不存在——**不要**先手工执行 demo.sql 再 `up`。
|
||
|
||
```bash
|
||
goose -env=.env.local validate # 离线确认文件可解析
|
||
goose -env=.env.local status # 显示 00001 Pending:连接成功,该迁移尚未应用
|
||
goose -env=.env.local up # 执行 00001,并在 goose_db_version 中记录版本:
|
||
# set_updated_at() 函数、users/categories/links 三张表、
|
||
# 约束、注释、索引与触发器
|
||
goose -env=.env.local status # 00001 显示应用时间
|
||
goose -env=.env.local version # 显示 version 1
|
||
```
|
||
|
||
- `up` 可重复执行:已应用版本按 `goose_db_version` 自动跳过。
|
||
- **已执行过的迁移文件不再修改**;要变更结构就新增迁移文件(见下节)。
|
||
|
||
## 日常新增迁移
|
||
|
||
```bash
|
||
goose -env=.env.local -s create add_example_column sql
|
||
```
|
||
|
||
`-s` 表示顺序编号:在现有 00001 基础上生成
|
||
`migrations/00002_add_example_column.sql`(实际编号按目录中现有最大版本
|
||
延续)。例如给 users 表加一列,编辑生成的文件:
|
||
|
||
```sql
|
||
-- +goose Up
|
||
ALTER TABLE public.users
|
||
ADD COLUMN avatar_url text NOT NULL DEFAULT '';
|
||
|
||
-- +goose Down
|
||
ALTER TABLE public.users
|
||
DROP COLUMN avatar_url;
|
||
```
|
||
|
||
然后按顺序执行:
|
||
|
||
```bash
|
||
goose -env=.env.local validate
|
||
goose -env=.env.local status
|
||
goose -env=.env.local up
|
||
goose -env=.env.local status
|
||
```
|
||
|
||
Up/Down 成对编写,保证迁移可回滚。
|
||
|
||
## 回滚警告
|
||
|
||
```bash
|
||
goose -env=.env.local down # ⚠ 危险操作,见下
|
||
```
|
||
|
||
- `down` 只回滚**最近一个**已应用迁移的 Down 部分,**不是**清库或重建的
|
||
通用命令。
|
||
- **回滚 00001 会删除 `links`/`categories`/`users` 三张表及表中全部数据。**
|
||
- 执行任何 `down` 之前:阅读对应迁移 SQL、评估数据损失、备份数据库。
|
||
不建议随手“试一下” `down`,也不要把它列入日常维护流程。
|
||
|
||
## 故障速查
|
||
|
||
| 报错特征 | 常见原因与处理 |
|
||
|---|---|
|
||
| `hostname resolving error`(主机名里混入非法字符) | DBSTRING 格式或 URL 编码不对:检查用户名/密码特殊字符是否已按上表编码、`://` 与 `@` `:` 分隔符是否完整 |
|
||
| `password authentication failed` | 用户名或密码不对:核对 `.env.local`(编码后)的值;再排查系统环境同名变量是否优先生效(只查变量名,不打印值) |
|
||
| `connection refused` / `i/o timeout` | 主机端口不可达:确认 PostgreSQL 实际监听地址、端口、容器端口映射与防火墙 |
|
||
| `already exists`(对象已存在) | 目标对象曾被手工创建(如执行过 demo.sql):先评估现有对象与迁移的差异再决定处理方式;**不要直接删数据**,也不要手工往 `goose_db_version` 插版本记录伪造状态 |
|