From Python to Go: Why We Rewrote Our Ingest Pipeline at Telemetry Harbor
我们将 Telemetry Harbor 的摄取管道从 Python FastAPI 重写为 Go,原因是遇到了严重的性能瓶颈。迁移后,效率提升了 10 倍,数据完整性因严格类型检查而得到加强,系统也拥有了稳定、可扩展的高并发时间序列数据摄取基础。
背景:打造一个时间序列数据平台
Telemetry Harbor 源自我们在汽车行业积累的经验。几乎每个项目都要重复搭建相同的基础设施:数据库、后端、数据摄取管道、可视化界面。每次都要花费数周时间,这让我们萌生了打造一个开箱即用平台的想法。
当时的市场方案并不理想。InfluxDB 的商业化策略让许多关键特性被锁在付费墙后,版本迁移成本高且在大数据负载下表现不佳。TimescaleDB 与 ClickHouse 技术上更强大,但依旧需要用户自行构建后端与摄取管道。我们看到了缺口——需要一个极简、可靠、可直接使用的平台。
Python FastAPI:原型开发的正确选择
MVP 阶段,我们在开发速度与运行性能之间权衡。最终选择了 Python FastAPI,因为它允许我们:
- 快速验证市场假设
- 迅速收集客户反馈并迭代
- 在低成本下尝试多种方案
- 尽快上线以抢占市场
早期架构非常直接:HTTP API(避免防火墙问题)、Redis + RQ 队列、TimescaleDB。测试效果良好,但很快暴露了性能隐患——RQ 的同步处理方式无法支撑高吞吐场景。
性能瓶颈:Python 无法跟上增长
随着数据量上升,性能问题逐渐浮现:
- 空闲 CPU 占用:10%
- 中等负载:约 40% CPU
- 高负载:120–300% CPU(峰值 800%),频繁崩溃
问题不仅在于 RQ 的同步限制,而是整个 Python 架构在常规负载下都难以维持稳定。这迫使我们考虑全面重写。
迁移决策:为什么选择 Go?
我们评估了 Rust 和 Go:
- Rust:性能极致,但学习曲线陡峭,开发周期长。
- Go:接近 Rust 的性能,开发简单,迭代快速。
最终选择 Go,以兼顾性能与开发速度。
重写摄取管道
我们使用 Go Fiber 框架实现新服务,确保与 Python 版本 API 兼容,并通过版本控制平滑过渡(v1 为 Python,v2 为 Go)。
性能提升显著:
- 空闲 CPU:1%(原 10%)
- 高负载 CPU:约 60%,无崩溃
- 性能提升 10 倍,系统行为可预测
应用层性能瓶颈彻底消除。
工程挑战:数据库约束处理
我们必须防止同一设备、货物、时间戳的重复数据。批量写入时,如果一条记录冲突,PostgreSQL 会拒绝整个批次。
解决方案是两阶段插入:
- 将数据写入临时表
- 由 PostgreSQL 从临时表中选择并插入合法记录
既保证性能,又维持数据完整性。
新瓶颈:数据库性能
随着应用层变得高效,PostgreSQL 成为主要性能瓶颈。这是好事,数据库扩展手段更多(分片、只读副本、水平分区),可支持更高吞吐。
意外发现:Pydantic 的类型强制转换
在 Go 版本中,400 错误率升高。调查发现,Python/Pydantic 会隐式转换类型:
- True/False → 1/0
- “123.45”(字符串) → 123.45(浮点)
Go 严格拒绝此类数据,暴露了 Python 版本未检测的数据质量问题。我们决定保留 Go 的严格校验,防止潜在数据污染。
现状与未来
目前两版 API 并行运行,新客户使用 Go v2。下一步将进行数据库分片以支持更大规模。
经验教训
- 快速原型,但要知道何时重写
- 性能不仅是速度,更是可预测性
- 类型安全能避免长期数据问题
- 严格校验比灵活容错更可靠
结论
Python 帮助我们快速验证市场并上线,而 Go 为我们提供了可扩展、稳定、可靠的生产基础。对于面临类似抉择的团队:当现有技术无法支撑下一阶段增长时,不要害怕重写。