当前位置:首页 > 文章列表 > Golang > Go教程 > Go Goose 怎样把 SQL 迁移文件嵌入 Go 可执行文件

Go Goose 怎样把 SQL 迁移文件嵌入 Go 可执行文件

来源:17golang原创 2026-10-29 11:20:22 0浏览 收藏

Goose 可以读取 Go 的 embed.FS:用 //go:embed migrations/*.sql 把迁移文件编入程序,再通过 goose.SetBaseFS 指定这个文件系统,调用 goose.UpContext(ctx, db, "migrations")。目录参数依然要写,因为内置文件系统保留了 migrations 这一层。

官方地址:https://github.com/pressly/goose

如果发布目标是“程序和 SQL 必须来自同一次构建”,我会优先选嵌入方案。这个判断来自交付约束,而不是某次线上事故:下面以一个独立的 PostgreSQL 迁移命令为例,说明文件、连接和执行职责怎样拆开。示例用于讲解,未附实际运行输出。

先按交付约束选择嵌入方案

我最看重的是减少交付项。SQL 和程序一起构建后,镜像无需再维护一份 migrations 目录的复制规则。但如果团队要求不重新构建就替换迁移脚本,外部目录更合适,嵌入反而增加发布成本。

考虑点嵌入 SQL外部目录
交付对象程序包含构建时的脚本副本程序和脚本目录分别交付
更新脚本重新编译、重新发布更新目录及其版本管理
运行时路径内置文件系统路径操作系统目录路径
适合的约束迁移内容随应用版本冻结脚本独立交付和管理

Goose 同时提供 CLI 和库。这里使用库构建自己的命令;单独安装通用 goose 命令,并不会自动把你的项目 SQL 打包进去。项目说明列出了嵌入迁移支持。

让迁移路径属于源码包

建立一个独立的迁移目录:cmd/dbmigrate/main.go 保存下面的 Go 程序,cmd/dbmigrate/migrations/00001_create_task_labels.sql 保存 SQL。两者的相对位置才是关键。embed 模式相对于声明它的源码包目录解释,不是相对于你敲 go build 命令的位置。

-- +goose Up
-- 示例业务表:记录任务使用的标签,不包含连接凭据。
CREATE TABLE task_labels (
    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    label TEXT NOT NULL UNIQUE
);

-- +goose Down
-- 回退会删除标签数据,仅在明确接受数据损失时使用。
DROP TABLE task_labels;

00001 是迁移版本前缀,-- +goose Up 标识升级内容,Down 部分说明回退操作。本例只创建一张普通表,使用 Goose 默认事务安排;不要把这种安排扩大成所有数据库 DDL 都能事务回滚的结论。

声明 migrations/*.sql 会保留匹配文件的目录层级。embed 路径使用正斜线,不能通过 ../ 跨到父目录。把 SQL 放到声明所在包的子目录,通常比依赖构建机的绝对路径清楚。更多模式限制见 Go embed 文档。

源码包与内置文件系统的路径结构图
结构图:源码包与内置资源分别成组,migrations 目录层级解释了为什么迁移目录参数仍需保留。

把内置文件系统接入迁移程序

下面的完整 main.go 使用 PostgreSQL 的 database/sql 驱动。项目需要声明 Goose v3 和 pgx v5 依赖,并把选择的具体版本固定在 go.mod、go.sum 中。embed 的语法支持和所选依赖版本的最低 Go 工具链要求是两回事,构建环境应满足后者。

package main

import (
    "context"
    "database/sql"
    "embed"
    "fmt"
    "log"
    "os"
    "time"

    _ "github.com/jackc/pgx/v5/stdlib"
    "github.com/pressly/goose/v3"
)

// 只嵌入随本次构建发布的 SQL,路径相对于当前源码包。
//go:embed migrations/*.sql
var migrationFiles embed.FS

func migrate() error {
    dsn := os.Getenv("MIGRATION_DATABASE_URL")
    if dsn == "" {
        return fmt.Errorf("缺少 MIGRATION_DATABASE_URL")
    }
    db, err := sql.Open("pgx", dsn)
    if err != nil {
        return fmt.Errorf("创建数据库连接池失败: %w", err)
    }
    // 在返回上层之前释放连接池;不要在此函数里直接退出进程。
    defer func() {
        if err := db.Close(); err != nil {
            log.Printf("关闭迁移连接池失败: %v", err)
        }
    }()

    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
    defer cancel()
    if err := db.PingContext(ctx); err != nil {
        return fmt.Errorf("连接目标数据库失败: %w", err)
    }

    // 本命令只有一个迁移入口,初始化后不再并发切换包级配置。
    goose.SetBaseFS(migrationFiles)
    if err := goose.SetDialect("postgres"); err != nil {
        return fmt.Errorf("设置数据库方言失败: %w", err)
    }
    // migrations 是内置文件系统中的目录,不是部署机器上的绝对路径。
    if err := goose.UpContext(ctx, db, "migrations"); err != nil {
        return fmt.Errorf("执行内置迁移失败: %w", err)
    }
    return nil
}

func main() {
    if err := migrate(); err != nil {
        // migrate 中的 defer 已执行,错误退出可交给发布系统处理。
        log.Print(err)
        os.Exit(1)
    }
    log.Print("迁移命令完成")
}

我会把三个参数分别记住:migrationFiles 决定从哪里发现脚本;"postgres" 决定 Goose 使用的数据库方言;db 才代表真实数据库连接。pgx 驱动的注册名与 Goose 方言名不同,不能因为连接池用 pgx 就把方言字符串也改为 pgx。

上述接口及 UpContext 的参数可查 Goose API 文档。这是一个专用命令的包级 API 示例,多套互不相同的迁移配置不宜在并发任务中反复调用全局设置。

把数据库资源留在发布配置中

程序里包含的是 SQL 字节,不是数据库。MIGRATION_DATABASE_URL、网络访问、目标数据库、迁移账号权限仍需由部署环境提供。连接串不要写进 SQL 或作为 embed 文件发布;能读取程序的人不应因此获得部署凭据。

Goose 内置脚本与外部数据库连接的依赖结构图
结构图:内置脚本、迁移配置和外部连接各有边界。连线表示依赖关系,不表示执行顺序或实测结果。

图中的 migrationFiles 与 SetBaseFS 相连,SetDialect 与 UpContext 相连;环境连接串配置 sql.DB,后者也是 UpContext 的依赖。文件嵌入不会消除这些运行条件。

在独立命令里设置两分钟超时,是本文的示例选择,实际应按迁移规模安排。上下文取消是停止工作的请求,不能当成“数据库必定已恢复原状”的证明。迁移失败后先确认目标库状态,再决定修复或重新执行。

发布时守住版本和执行责任

推荐将这个命令作为发布阶段的单独任务,由发布系统明确指定执行者。嵌入只统一了制品内容,并没有自动实现多副本协调;不要把每个应用副本同时启动迁移当作文件嵌入功能的一部分。

提交新 SQL 后重新构建 ./cmd/dbmigrate。构建过程需要包含 migrations 子目录,发布制品则可以只包含构建出的命令及其正常运行依赖。是否能使用完全空白的容器基础镜像,还受驱动、证书和系统依赖影响,不能仅凭 embed 下结论。

已发布的旧迁移应保持稳定,用新的版本文件追加变更。只修改已执行版本的 SQL 内容,不会自动表达一项新的数据库升级。对于不可逆的数据变更,回退计划还应覆盖备份或补偿,本文的 DROP TABLE 示例不能替代生产恢复方案。

为什么 SQL 已经嵌入,还找不到目录?

先比较声明中的 migrations/*.sql 与 UpContext 的目录参数。普通 embed.FS 保留 migrations 层级,所以这里传 "migrations";如果你另外用 fs.Sub 改变了根目录,则必须按改变后的根重新选择目录,不能机械照抄。

运行后修改旁边的 SQL 文件会生效吗?

不会改变这个程序已经嵌入的副本。修改源码中的 SQL 后需要重新构建并部署对应制品。我的取舍是:希望脚本与程序绑定,就接受这项构建成本;希望独立热更新脚本,就选择外部目录管理。

可以往 embed.FS 里 create 新迁移吗?

嵌入资源提供只读访问。Goose 的 create、fix 等修改操作仍针对操作系统文件,不会重写正在运行的可执行文件。开发阶段在源码目录创建迁移,发布阶段读取编译进去的迁移,职责分开后更容易维护。这个限制见 官方嵌入说明。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go SQLX 怎样把命名参数与 IN 列表展开组合起来Go SQLX 怎样把命名参数与 IN 列表展开组合起来
上一篇
Go SQLX 怎样把命名参数与 IN 列表展开组合起来
Go Goose 怎样为包含分号的存储过程标记语句范围
下一篇
Go Goose 怎样为包含分号的存储过程标记语句范围
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    773次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    770次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    852次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    821次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    651次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码