基于 Go‑Zero 的用户 CRUD Demo:如何一步步从 MySQL + sqlx 演进为 PostgreSQL + GORM + 微服务架构

本文以 GitHub 仓库 big‑dudu‑mosty/go_zero_testpostgres 分支)为蓝本,详细拆解如何在现有 go-zero 单体示例上完成数据库迁移、ORM 替换,以及微服务拆分。你可以参考该仓库的 commit 历史commits/postgres)逐阶段对照学习。


一、整体演进路线回顾

这个项目主要分为几个阶段:

  1. MySQL + sqlx 单体版本(项目初始阶段)

  2. 切换到 PostgreSQL + GORM(实现持久化层升级)

  3. 拆分微服务 :拆为 user-api(HTTP)和 user-rpc(RPC 服务)

  4. RPC 通信:API 层通过 zrpc 调用 RPC 服务完成业务

  5. 整理 go-zero 结构:ServiceContext、proto、goctl 等配合使用

通过这些阶段,你可以理解:为什么要迁移数据库、为什么用 GORM、为什么拆服务,以及拆完怎样组织代码。

⚡ 想跟着实践的朋友,可以直接访问源码:big-dudu-mosty/go_zero_test

仓库里 commit 历史清晰,适合按阶段学习和调试。如果觉得教程有帮助,顺手点个 Star ⭐ 支持一下,让我继续更新更多 go-zero 实战内容。


二、如何使用 commit 历史学习( strongly 推荐这样做)

在你的仓库里,postgres 分支的 commit 历史非常清晰。建议读者:

  1. git clone 后切换到 postgres 分支

    复制代码
    git clone https://github.com/big-dudu-mosty/go_zero_test.git  
    cd go_zero_test  
    git checkout postgres  
  2. 使用 git log --oneline 或者图形化工具(如 GitKraken、GitHub web 界面)查看每一个 commit。

    • 你可以看到某些 commit 明确标注为 "switch to postgres"、 "add GORM model"、 "split service into api + rpc" 等。

    • 建议读者 按 commit 顺序执行或阅读,这样就像在做一个分阶段的实践,而不是一次性看最终代码。

这样做有几个好处:

  • 能明确哪些改动是为了数据库迁移,哪些是微服务拆分。

  • 有助于理解改动背后的 "为什么" ------ 而不是只看最终状态。

  • 如果自己按类似路径改造自己的项目,也可以照着历史一步步来。


三、阶段详解与关键技术点、易错点

下面我按阶段拆解整个演进过程,并强调每一步常见坑、注意事项。

3.1 阶段一:MySQL + sqlx 单体 Demo(起点)

  • 初始结构:只有一个服务(单体 app),Handler → Logic → Model → sqlx 操作 MySQL。

  • 这个阶段是稳定基础,也是开发者理解业务逻辑、接口定义和 go-zero 架构分层的基础。

注意点

  • sqlx 的查询语法比较原始,自写 SQL 时容易拼错占位符(?)。

  • sqlx 插入并获取主键常用 LastInsertId(),但这个在切换到 PostgreSQL 后会变成问题。

  • 如果初始数据模型设计不合理(如缺乏 created_at/updated_at 字段),后面 ORM 转换时可能得补表结构。


3.2 阶段二:切换到 PostgreSQL + GORM

这是一个非常关键但容易出错的阶段。下面是详细拆解:

3.2.1 修改配置
  • 更新 go-zero 配置文件(比如 user-rpc/etc/user.yaml)中的数据库连接字符串为 Postgres,比如:

    复制代码
    DataSource: "postgres://user:password@localhost:5432/dbname?sslmode=disable"
  • 注意:sslmode 设置、端口、用户名密码等都必须和你的 Postgres 本地 / 容器环境对应。

3.2.2 GORM 引入和 Model 层重构
  • 创建 GORM *gorm.DB 实例,并在 ServiceContext 中注入。

  • 重写 model/ 层,把原 sqlx CRUD 迁移到 GORM。

    • 对象定义(struct)要带上 GORM tag,例如 gorm:"column:id;primaryKey;autoIncrement"

    • 插入时,GORM 默认会处理 RETURNING id

    • 查询方法(Find / First / Where)要写清楚条件。

常见坑

  • 大小写问题 :GORM 对字段名和数据库列默认会做一定映射,但如果你手动写了 column: tag,一定要对照 Postgres 表定义。

  • 占位符问题 :在纯 SQL 自定义查询里(如果有 Raw SQL)要注意 Postgres 占位符是 $1, $2...,不能继续写 ?

  • 事务处理 :GORM 的事务和 go-zero 的上下文结合时,要确保在逻辑层正确传递 *gorm.DB,不要误用全局 DB。

3.2.3 数据迁移与兼容
  • 如果你从 MySQL 迁移真实数据到 Postgres,需要手写迁移脚本 (SQL) 或者使用迁移工具。你可以在项目里查看或扩展迁移逻辑。

  • 测试迁移后 CRUD 接口是否正常 ------ 建议编写集成测试,用真实 Postgres 实例来跑。

3.2.4 commit 回顾
  • 在 commit 历史中,你会看到为了这一阶段引入的 commit ------ switch to postgresadd GORM model 等。读者可以定位这些 commit 来理解每一步改动。

3.3 阶段三:微服务拆分 --- API 与 RPC

这一步是构建真正 "微服务 + go-zero 标准结构" 的关键。

3.3.1 拆分服务目录
  • user-api/:只负责 HTTP 接收请求 → 验证 / 解析 → 调用 RPC Client。

  • user-rpc/:负责业务逻辑 + 数据访问 + GORM + 持久化。

目录结构类似:

复制代码
user-api/
  internal/handler
  internal/logic
  internal/svc
user-rpc/
  internal/logic
  internal/svc
  model/
  proto/user.proto
3.3.2 定义并生成 RPC 接口
  • user-rpc/user.proto 中定义 RPC 方法,例如 CreateUser, GetUser, ListUsers 等。

  • 用 go-zero 的 goctl 生成代码:

    复制代码
    goctl rpc protoc user.proto --go_out=./internal --go-grpc_out=./internal --zrpc_out=.
  • 生成后的代码会包含服务端和客户端 stub、ServiceContext scaffold。

注意点 / 易错点

  • .proto 文件定义时要考虑输入输出结构是否合理,例如分页查询要带 limit/offset

  • goctl rpc protoc 时路径必须在服务模块根目录下,否则生成路径可能混乱。

  • 每次改 .proto 后重新生成代码,不要手动修改生成文件中的业务逻辑。

3.3.3 在 user-api 调用 RPC
  • 在 API 层 logic 中注入 RPC client,例如:

    复制代码
    resp, err := l.svcCtx.UserRpc.CreateUser(l.ctx, &userRpc.CreateUserReq{
        Name: req.Name,
        Age:  req.Age,
    })
  • API 层不再访问数据库,不再依赖 GORM / sqlx 逻辑。职责清晰。

注意

  • 错误处理非常关键。RPC 返回错误时,API 要做恰当转换(HTTP 状态码 + 返回内容)。

  • 上下文(context)要传递好(例如 l.ctx),以便在 RPC 层做超时、取消。

3.3.4 启动脚本与设置
  • 你的仓库中有 start-services.sh / stop-services.sh,读者可以用它启动整个系统(API + RPC)。

  • 启动前请检查配置文件(API 和 RPC 分别有自己的 etc 配置 yaml),确认数据库连接、RPC 端口、日志等配置正确。


四、易错点 & 实践建议(经验总结)

下面是从这个项目 +微服务 +迁移实践中提炼出的 踩坑建议

  1. 搞错占位符

    • sqlx 到 GORM 时,如果还写原来 sql 部分,自定义 SQL 中可能误用 ?,导致 Postgres 报错。

    • 推荐对 Raw SQL 使用 $1, $2...,或者尽量避免 Raw SQL,优先使用 GORM API。

  2. ID 插入与返回

    • PostgreSQL 插入记录后,如果你想拿到 ID,一定要使用 RETURNING id。GORM Create 默认能处理,但如果用 ExecRaw 等要注意。

    • 在事务中插入多个表时,要处理事务提交失败 /回滚。

  3. 上下文传递失误

    • 在 go-zero ServiceContext 和 logic 里,不要丢掉 ctx,否则你无法控制 RPC 超时,也不利于 trace。

    • RPC client 和 server 之间要有合理的超时策略。

  4. proto 更新不及时

    • 每次你改用户结构(比如新增字段)后,proto 也要同步更新。否则 API 与 RPC 结构不匹配。

    • 生成代码后,不要手动修改生成的 .pb.go,业务逻辑都写在 internal/logic

  5. 依赖注入 & 初始化

    • ServiceContext 要注入 GORM DB 连接、RPC Client、配置对象。

    • 启动初始化顺序要正确:RPC 服务启动时先连接数据库、迁移(如需要),然后才开始监听;API 服务启动时,要确保 RPC 客户端能连接。

  6. 测试覆盖

    • 建议写集成测试:启动 Postgres(可以用 Docker)、启动 RPC 服务、调用 API 接口,验证整个链路。

    • 针对 GORM Model 层写单元测试,对 CRUD 各种边界条件(空字段、重复、分页)都覆盖。

  7. 版本控制策略

    • 用好仓库的 commit 历史。读者可以通过 git checkout <commit> 返回到某个阶段来调试 /观察。

    • 推荐你在每个关键阶段 commit 时写清晰且有意义的 commit message(例如 "migrate to Postgres", "introduce GORM models", "split into RPC service")。


五、后续可扩展方向(对读者的建议)

在完成上述迁移和拆分之后,这个项目已经非常有示范意义。接下来你还可以考虑:

  • 集成 分布式链路追踪(如 Jaeger / Zipkin),让 API → RPC 调用可视化

  • 引入 限流 / 熔断 / 断路器(go-zero 本身支持)

  • 实现 鉴权(JWT / OAuth),在 API 层做认证,RPC 层做权限校验

  • 数据库迁移管理:使用迁移工具(如 golang-migratepgmigrate)持续管理 Postgres schema

  • 增加 监控(Prometheus + Grafana):监控 RPC 调用次数、延迟、错误率等

  • 支持 分页 / 搜索、复杂查询、事务、批量操作


六、小结

  • 通过仓库(postgres 分支)和 commit 历史,我们可以 系统地学习 go-zero 项目的演进:从单体 MySQL → PostgreSQL + GORM → 拆分 API + RPC。

  • 每一步都有技术要点和容易出错的地方:数据库语法差异、ORM 模型设计、RPC 接口定义、上下文管理等。

  • 建议读者强烈依赖 commit 历史,按阶段对照阅读和实践,这样理解最透彻。

  • 这个项目不仅适合 go-zero 初学者,也适合中级开发者用来练微服务拆分、架构升级。

相关推荐
e***0961 小时前
【MySQL】MySQL库的操作
android·数据库·mysql
4***V2021 小时前
MySQL查询执行计划
android·mysql·adb
h***93662 小时前
【MySQL数据库】Ubuntu下的mysql
数据库·mysql·ubuntu
绝无仅有2 小时前
面试日志elk之ES数据查询与数据同步
后端·面试·架构
h***8562 小时前
MySQL数据分析
数据库·mysql
绝无仅有2 小时前
大场面试之最终一致性与分布式锁
后端·面试·架构
Mr.徐大人ゞ3 小时前
8.事务在 Group Replication 中的处理流程
mysql
i***27954 小时前
【golang学习之旅】使用VScode安装配置Go开发环境
vscode·学习·golang
a***97684 小时前
从MySQL迁移到PostgreSQL的完整指南
数据库·mysql·postgresql