- 使用 pgx/sqlx 建立连接,从环境变量读取数据库密码 - 添加本地 PostgreSQL Compose 配置与初始迁移 - 补充 Goose 操作手册和连接串排错说明
7.3 KiB
Goose 迁移操作手册
Lyra 数据库迁移的唯一完整操作手册。migrations/ 目录是数据库结构的
唯一正式执行来源;demo.sql 仅保留作参考,不要再手工执行它。
迁移目录本身的说明见 migrations/README.md。
目录与前提
-
所有 goose 命令都在 Lyra module 根目录(
migrations/的上一级)执行:cd go/projects/lyra # 从仓库根出发 -
目标数据库已创建:迁移不负责建库,
lab库需先存在。 -
首次迁移要求目标对象尚不存在:如果之前手工执行过 demo.sql 建过 同名对象,
up会报already exists,先按故障表评估处理。
安装 goose CLI(固定 v3.29.0)
goose 只作为独立命令行工具使用(Go 应用尚未引入 goose 依赖):
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:export PATH="$PATH:$(go env GOPATH)/bin" # 可写入 ~/.bashrc
配置连接:.env.local
goose 不读取应用的 etc/lyra.toml,也不会只凭 PG_PASSWORD 之类的
应用环境变量自动拼出连接串——它只认自己环境变量里的 GOOSE_DBSTRING。
在 module 根创建 .env.local 并追加以下内容(全部是占位值,换成实际
信息;lab 库需已创建):
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仍行为异常时,先查是否存在同名 环境变量——只列变量名、不打印值,避免泄露连接串:env | cut -d= -f1 | grep -i '^GOOSE'
连接串特殊字符:URL 编码
GOOSE_DBSTRING 是 URL 格式:用户名、密码中的特殊字符必须逐个做
百分号编码,否则主机名/路径解析会错位。
- 对用户名和密码分别编码,不要把整串 URL 一起编码,也不要为迁就 DSN 去修改数据库里的实际密码。避免对已经编码的内容重复编码。
- 不要把真实密码交给在线网站转换;按下表手工替换即可。
| 字符 | 编码 | 字符 | 编码 |
|---|---|---|---|
@ |
%40 |
: |
%3A |
# |
%23 |
/ |
%2F |
% |
%25 |
? |
%3F |
示例:密码 demo#pass(演示值)在 DSN 中写成 demo%23pass。
常用命令
# 离线检查迁移文件能否被 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。
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自动跳过。- 已执行过的迁移文件不再修改;要变更结构就新增迁移文件(见下节)。
日常新增迁移
goose -env=.env.local -s create add_example_column sql
-s 表示顺序编号:在现有 00001 基础上生成
migrations/00002_add_example_column.sql(实际编号按目录中现有最大版本
延续)。例如给 users 表加一列,编辑生成的文件:
-- +goose Up
ALTER TABLE public.users
ADD COLUMN avatar_url text NOT NULL DEFAULT '';
-- +goose Down
ALTER TABLE public.users
DROP COLUMN avatar_url;
然后按顺序执行:
goose -env=.env.local validate
goose -env=.env.local status
goose -env=.env.local up
goose -env=.env.local status
Up/Down 成对编写,保证迁移可回滚。
回滚警告
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 插版本记录伪造状态 |