从 PHP 到 Golang:我们为什么选择了迁移
背景与动机
现状痛点
我们的 API 服务早期主要基于 PHP ,部署在传统 LAMP 环境。随着业务增长,并发请求量上升,PHP 的并发处理能力不足成为明显瓶颈。后期部分服务迁移到 Java ,但 Java 的内存占用问题又带来了新的运维压力。
目前新的 API 参考实现已经就绪,但使用 MySQL 的服务尚未完全迁移到新平台。主要原因是:
- 数据库错误处理:需要确保新平台能妥善处理各种数据库异常情况
- 安全模型升级:现有安全模型需要升级以满足新的安全要求
迁移目标
- 零停机部署:API 版本更新时服务不中断
- 容器化管理:统一开发、测试、生产环境
- 性能提升:降低内存占用,提高并发处理能力
- 运维简化:一键部署、快速回滚
技术选型对比
| 维度 | PHP/Laravel | Java/Spring Boot | Golang/Gin |
|---|---|---|---|
| 内存占用 | 128-256MB | 512MB-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 镜像作为最终运行环境。构建流程分两步:
- 本地交叉编译:将 Go 代码编译为静态二进制文件
- COPY 进 distroless 镜像:distroless 镜像不包含 shell、包管理器等,攻击面极小,最终镜像约 5MB
容器编排
使用 Docker Compose 进行容器编排,主要特点:
- 配置外置:配置文件、静态资源通过 volume 挂载,修改后重启即可生效
- 时区同步:挂载宿主机
/etc/localtime确保容器内时区一致 - 自动重启:设置
restart: unless-stopped保证服务高可用
旧版 PHP 接口
部分接口仍使用 PHP 实现,主要集中在依赖 MySQL 数据库的业务逻辑。这些接口将在后续阶段逐步迁移至 Golang。目前旧版API仍在运行。
部署与运维
更新流程
当前项目采用单容器部署,更新流程如下:
- 本地交叉编译:在开发机上编译出 Linux 二进制文件
- 构建新镜像:基于 distroless 基础镜像构建
- 重启服务:通过
docker-compose down && docker-compose up -d完成更新 - 验证服务:确认容器状态正常,API 响应正确
配置管理
配置文件通过 volume 挂载到容器内,修改后只需重启容器即可生效,无需重新构建镜像。这种设计使得配置变更非常轻量。
日志方案
Gin 框架内置了请求日志中间件,配合 logrus 的自定义格式化输出,支持结构化日志记录,方便排查问题。
迁移步骤详解
当前进度:大部分 API 已迁移到 Golang + Docker 架构。使用 MySQL + Redis 的旧 PHP 服务尚未迁移,主要卡点在于数据库错误处理的健壮性验证和安全模型升级。
阶段一:环境准备
- 安装 Docker 及 Docker Compose
- 克隆项目并配置环境变量
- 启动服务
阶段二:纯 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 文件备份,重启服务即可完成完全替换。
性能对比数据
资源占用对比
| 指标 | PHP | Golang |
|---|---|---|
| 内存占用 | 128-256MB | 10-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 进行许可。