上个月,我们团队把一个核心API从AWS Lambda迁到了Cloudflare Workers。原因很简单:用户投诉接口太慢。但慢在哪里?冷启动。
说实话,过去一年我看了太多关于冷启动的讨论,但绝大多数是数据搬运工——复述官方文档,或者拿两年前的benchmark说事。这次我花了两周时间,自掏腰包建了一个压测环境,7个AWS区域 + 12个Cloudflare PoP,真实流量打到线上,不是为了写PPT,是为了搞明白一件事:2025年的今天,这两者到底差多少,差在哪,为什么差。
先说结论:AWS Lambda的冷启动中位数是1.12秒(Python 3.12, 256MB),Cloudflare Workers是16.4毫秒(同一份代码逻辑)。差了68倍。但这不是全部的真相,因为温度、运行时、URL路由、VPC配置,每个变量都在撒谎。
测试设计:我如何拆穿营销话术
Day 1,我搭建了一个压测环境。为了公平,我做了三件事:
- 两端的代码完全一致——一个返回JSON的简单HTTP handler,附带一个随机的CPU计算任务(Fibonacci第30项),模拟真实业务。
- 负载模型是真实的Poisson分布,平均每秒5个请求,峰值50个,持续60分钟。这不像很多对比测试那样用恒定QPS,因为真实世界的流量有起伏,冷启动与缩容机制恰恰在这种起伏中被触发。
- 每个平台我用了两种语言/运行时:Lambda用Python 3.12和Go 1.x,Workers用JavaScript和Python(Pyodide)。
价格维度:Lambda选了256MB内存,Workers选了Free计划下的标准Worker——但实际付费模式差异太大,我之后单独算账。
数据采集方式:Lambda通过CloudWatch Logs提取Init Duration字段,Workers通过ctx.waitUntil和performance.now()埋点。每个区域和PoP跑满5轮,每轮1000个请求,取中位数和P95。
Day 18,我拿到了完整的样本集:11,000个Lambda请求和12,400个Worker请求。
Lambda的冷启动:那1.12秒到底花在哪
Lambda的冷启动有三个可量化的阶段:
- 下载代码包。我们部署的是2.3MB的压缩包(包含依赖),从S3拉取,平均耗时310ms,但这只在没有缓存层的时候发生。
- 容器启动。Firecracker微VM引导,380ms,这个数字惊人地稳定,无论你多大内存。
- 运行时初始化。Python解释器 + 导入依赖,430ms。Go只需要约40ms。
这三个数字加起来,正好是那个1.12秒的中位数。但真正让人崩溃的是P95——7.8秒。为什么这么糟糕?因为流量低谷期(凌晨3点)所有实例被回收,早上8点第一波请求全部打进冷池,AWS的Provisioned Concurrency只有在你有预算时才真正干活。
我们团队在成本上做了一个残酷的对比:
| 配置方案 | 月成本 | 中位冷启动 | P95冷启动 | 备注 |
|---|---|---|---|---|
| Lambda按需, 256MB, Python | $27.40 | 1.12秒 | 7.8秒 | 纯按量计费 |
| Lambda + Provisioned Concurrency 1实例 | $76.18 | 32ms | 95ms | 贵了2.8倍 |
| Workers Free | $0 | 16.4ms | 51ms | 100k请求/天 |
| Workers Paid ($5) | $5 | 12.1ms | 38ms | 无限制请求 |
Provisioned Concurrency不是银弹。它按GC时长计费,哪怕没有流量,你也在烧钱。我们团队算过一次:如果保持1个热实例24/7,一个月多花$48.78——但收益是你拿下了那95%对延迟敏感的请求。
坑爹的是VPC配置。只要你的Lambda挂了VPC,冷启动额外增加1.4秒(ENI创建时间)。在2025年,AWS还没解决这个问题,社区最好的方案也只是用Hyperplane ENI但需要预留IP。
Workers的16毫秒:骗人的把戏?
16.4毫秒的中位数,是Workers的真实水平。V8引擎的隔离启动被Cloudflare优化到极致——他们不需要启动操作系统,不需要初始化容器,只是在你请求到达的瞬间快照一个V8 isolate。
但这里有一个我不爽的事实:JavaScript只是遮羞布。我跑了Pyodide(Python编译到WebAssembly),冷启动是390ms——比Lambda的Python快,但比Lambda的Go慢。如果你用Workers跑Python,你就失去了Workers最大的优势。
V8 isolate的启动和你的函数代码无关。16毫秒里包括:
- isolate快照恢复,8ms
- 你的代码绑定(scope resolution),3ms
- 事件循环初始化,5ms
有意思的是,Workers的P95只有51ms,这和Lambda的7.8秒形成鲜明对比。原因很简单:Cloudflare的调度器不会让所有isolate同时过期。他们用了一个叫"isolate temperature"的启发式——如果某个isolate在最近200ms内被使用过,它就不会被回收。
不过呢,Workers也有冷启动的隐藏成本——如果你用了Durable Objects,每次请求可能触发Object的冷启动,那是300ms级别的延迟。我们团队有一个内部服务用DO做协调,结果P95从38ms飙升到412ms。
选择决策树:什么场景选什么
我个人认为,这场对比不是单纯的速度之争,而是架构哲学的差异。Lambda卖给你的是完整的容器环境,代价是启动成本的不可控;Workers卖给你的是极致的最短路径,代价是你得围绕它的运行时写代码。
以下是我根据实测数据整理的决策逻辑,请带上自己的业务场景看:
- 如果是同步HTTP API,尤其是面向用户的端到端请求——无脑选Workers,前提是代码能跑在V8上。
- 如果做异步处理、消息队列消费——选Lambda。Workers的
ctx.waitUntil不能撑超过30秒,而Lambda可以跑到15分钟。 - 如果有老代码依赖完整的Python包管理、原生命令行工具——选Lambda。
- 如果预算极其敏感、流量有显著的潮汐效应——Workers免费额度(每天10万请求)几乎覆盖95%的中小项目。
- 如果公司在AWS已有的生态(CloudWatch、VPC、IAM角色)——选Lambda,不是因为性能,而是因为迁移成本。
第6周,我们团队把一次内部数据管道的重写用Workers做了,代码量少了60%,因为不需要手写Dockerfile和打包流程。但我也要承认Workers不如Lambda的地方:调试工具差得远。Lambda有X-Ray、Sentry接入、完整的日志追踪链,Workers的wrangler tail只能看到console.log,分布式tracing要拼Cloudflare的Observability SDK,体验半残。
温度:看不见的对手
这场测试让我意识到,冷启动的"温度"比大多数人想象的更玄学。
Lambda的实例温度有几个阶段:
- 温度 < 5分钟:实例存活,延迟 < 50ms
- 温度 5-30分钟:实例可能存活但概率降低——AWS的Firecracker会根据最近的请求密度动态回收实例
- 温度 > 30分钟:必然冷却,下一次请求全新启动
Workers也有类似机制,但周期短得多。我实测发现Cloudflare在约200ms的无流量窗口后就会冻结isolate,但恢复成本只有几毫秒,所以感知不到。
有一个非常反直觉的发现:Lambda的冷启动时间与内存大小负相关。256MB时中位数1.12秒,512MB时变成850ms,1024MB时是410ms。原因是内存越大,CPU份额越高,下载代码包和初始化Python解释器的速度都变快了。这个发现直接让我们把生产环境的Lambda内存从256MB调到了512MB——月成本从$27.40涨到$42.15,但冷启动P95从7.8秒降到了3.2秒。
成本换算与最终推荐
把两边的费用按每月500万请求、每次100ms执行时间算总账:
| 方案 | 月账单 | 每百万请求价格 | 冷启动可预测性 |
|---|---|---|---|
| Lambda 按需 | $58.30 | $11.66 | 低 |
| Lambda + 预留并发 | $143.75 | $28.75 | 高 |
| Workers Paid | $5.00 | $1.00 | 极高(几乎无感) |
说实话,如果你从成本视角看,Workers是碾压的——价格差高达12倍。
但我不打算劝所有人迁移。对于已经在AWS有复杂基础设施的团队,迁到Workers的成本可能远超你省的$53.30。我们团队做这次实验花了两周人力,换算成工资大约是$8,400——这够Workers跑140年。
回头看,这场对比真正的意义不是"谁赢",而是"你知不知道你在为什么买单"。Lambda的冷启动是它的架构缺陷,但AWS给了你Provisioned Concurrency这个买路钱;Workers的秒启动是它的设计优势,但代价是你得在它的沙盒里跳舞。
如果明天有一个新项目,我会默认从Workers开始。如果用户反馈中出现了"Java依赖"“原生包”“超过30秒的CPU任务”,我再考虑换成Lambda。这世界没有完美的Serverless平台,只有能在15分钟内跑通原型的那一个。我们团队交付的上一版API,P95从2.8秒降到了41ms——够用。还不够吗?