从 PHP 到 Golang:我们为什么选择了迁移

从 PHP 到 Golang:我们为什么选择了迁移

MoGuQAQ Lv4

背景与动机

现状痛点

我们的 API 服务早期主要基于 PHP ,部署在传统 LAMP 环境。随着业务增长,并发请求量上升,PHP 的并发处理能力不足成为明显瓶颈。后期部分服务迁移到 Java ,但 Java 的内存占用问题又带来了新的运维压力。

目前新的 API 参考实现已经就绪,但使用 MySQL 的服务尚未完全迁移到新平台。主要原因是:

  • 数据库错误处理:需要确保新平台能妥善处理各种数据库异常情况
  • 安全模型升级:现有安全模型需要升级以满足新的安全要求

迁移目标

  • 零停机部署:API 版本更新时服务不中断
  • 容器化管理:统一开发、测试、生产环境
  • 性能提升:降低内存占用,提高并发处理能力
  • 运维简化:一键部署、快速回滚

技术选型对比

维度PHP/LaravelJava/Spring BootGolang/Gin
内存占用128-256MB512MB-1GB(占用高)20-50MB
启动时间2-5秒10-30秒0.5-1秒
并发处理良好优秀
容器镜像500MB+300MB+10-20MB
学习曲线

技术栈概览

层级技术选型说明
Web 框架Gin高性能 HTTP 框架,路由扁平注册
日志logrus结构化日志,支持自定义格式化
配置YAML通过 volume 挂载,支持热更新
容器化Docker + distroless最终镜像约 5MB,攻击面极小
数据存储MySQL + Redis密码服务使用,尚未迁移到 Go

Docker 容器化配置

镜像构建策略

项目采用 distroless 镜像作为最终运行环境。构建流程分两步:

  1. 本地交叉编译:将 Go 代码编译为静态二进制文件
  2. COPY 进 distroless 镜像:distroless 镜像不包含 shell、包管理器等,攻击面极小,最终镜像约 5MB

容器编排

使用 Docker Compose 进行容器编排,主要特点:

  • 配置外置:配置文件、静态资源通过 volume 挂载,修改后重启即可生效
  • 时区同步:挂载宿主机 /etc/localtime 确保容器内时区一致
  • 自动重启:设置 restart: unless-stopped 保证服务高可用

旧版 PHP 接口

部分接口仍使用 PHP 实现,主要集中在依赖 MySQL 数据库的业务逻辑。这些接口将在后续阶段逐步迁移至 Golang。目前旧版API仍在运行。

部署与运维

更新流程

当前项目采用单容器部署,更新流程如下:

  1. 本地交叉编译:在开发机上编译出 Linux 二进制文件
  2. 构建新镜像:基于 distroless 基础镜像构建
  3. 重启服务:通过 docker-compose down && docker-compose up -d 完成更新
  4. 验证服务:确认容器状态正常,API 响应正确

配置管理

配置文件通过 volume 挂载到容器内,修改后只需重启容器即可生效,无需重新构建镜像。这种设计使得配置变更非常轻量。

日志方案

Gin 框架内置了请求日志中间件,配合 logrus 的自定义格式化输出,支持结构化日志记录,方便排查问题。

迁移步骤详解

当前进度:大部分 API 已迁移到 Golang + Docker 架构。使用 MySQL + Redis 的旧 PHP 服务尚未迁移,主要卡点在于数据库错误处理的健壮性验证和安全模型升级。

阶段一:环境准备

  1. 安装 Docker 及 Docker Compose
  2. 克隆项目并配置环境变量
  3. 启动服务

阶段二:纯 Go 接口迁移(已完成)

目前所有不依赖 MySQL 的 API 接口已全部从 PHP 迁移到 Golang,运行稳定。迁移后内存占用大幅下降,并发处理能力显著提升。

阶段三:MySQL 服务迁移(进行中)

使用 MySQL + Redis 的旧 PHP 服务是迁移难度最高的部分,主要面临两个核心挑战:

1. 数据库错误处理

原 PHP 代码中大量使用 die() 直接输出错误,迁移到 Golang 后需要结构化的错误处理。主要挑战包括:

  • PHP 中 null 值在 JSON 序列化时的行为与 Golang 不同
  • 连接池行为差异:PHP 每次请求新建连接 vs Golang 长连接复用
  • Redis 缓存与 MySQL 的一致性需要重新设计

2. 安全模型升级

原有 PHP 代码存在安全隐患,迁移到 Golang 时需要全面升级:

  • 数据库连接信息需要从硬编码改为配置文件外部化
  • SQL 注入防护需要使用参数化查询
  • 敏感数据需要加密存储
  • 输入校验需要通过中间件统一处理

这些安全升级需要在 MySQL 服务迁移前完成,否则会将原有安全风险带入新架构。

阶段四:完全替换旧 PHP

当 MySQL 服务迁移完成后,将旧 PHP 文件备份,重启服务即可完成完全替换。

性能对比数据

资源占用对比

指标PHPGolang
内存占用128-256MB10-30MB
启动时间2-5秒0.5秒
容器镜像大小500MB+~5MB (distroless)
并发处理优秀

实际效果

迁移后的 Golang 服务在相同硬件上:

  • 内存占用从 200MB+ 降至 10-20MB
  • 容器镜像从 500MB+ 缩小到 ~5MB(distroless + 静态编译)
  • 启动时间从秒级降至毫秒级
  • 并发处理能力显著提升,不再受 PHP-FPM 进程数限制
  • 标题: 从 PHP 到 Golang:我们为什么选择了迁移
  • 作者: MoGuQAQ
  • 创建于 : 2026-05-05 15:04:01
  • 更新于 : 2026-05-05 15:04:01
  • 链接: https://blog.moguq.top/posts/26050501/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论