Hono 工程化实战 03:性能优化从哪里下手
Hono 很快。
但这不代表你的 Hono 应用一定快。
框架快,只说明框架本身的开销小。
真正的接口耗时通常花在这里:
1 | 数据库查询 |
所以 Hono 项目做性能优化,不能只盯着框架 benchmark。
要从真实请求链路下手。
这篇文章讲 Hono API 性能优化的实际顺序:先测量,再减少无效工作,再缓存,再优化数据库,最后才考虑运行时和部署形态。
技术栈快照时间:2026-06-23。Hono middleware、compress、timing、各运行时适配器会更新,具体行为以官方文档为准。
一、先测量,不要先猜
性能优化最常见的错误是凭感觉。
比如:
1 | 我觉得是 Hono 慢 |
这都没用。
先加基础耗时日志:
1 | app.use('*', async (c, next) => { |
然后对慢操作分段:
1 | const user = await measure('db.user.find', () => users.findById(userId)) |
没有测量就优化,基本是在碰运气。
二、先减少不必要的 middleware
Hono middleware 很好用。
但不要把所有 middleware 都挂到 *。
错误示例:
1 | app.use('*', auth()) |
这样连 /health、/public/* 都要跑一堆逻辑。
更合理:
1 | app.use('/api/*', requestId()) |
公共接口不要跑私有接口的鉴权。
健康检查不要查数据库。
静态资源不要跑业务 middleware。
性能优化第一步不是写缓存。
是少做没必要的事。
三、middleware 顺序也影响性能
推荐顺序:
1 | requestId |
为什么 bodyLimit 要早?
因为大请求应该尽早拒绝。
为什么权限检查在业务 handler 前?
因为没权限就不要查业务数据。
为什么 requestId 要最早?
因为后面的日志都需要它。
顺序不是洁癖。
顺序会影响失败请求的成本。
四、压缩不要重复做
Hono 有 compress middleware:
1 | import { compress } from 'hono/compress' |
但不是所有运行时都需要你手动压缩。
Hono 文档提醒,在 Cloudflare Workers、Deno Deploy 这类平台上,响应体可能已经由平台处理压缩。
所以要先确认部署环境。
如果你在 Node.js 自建服务上,compress middleware 可能有价值。
如果你在边缘平台上,重复压缩可能没有意义,甚至增加开销。
性能优化里有一条很重要:
1 | 不要优化已经由平台处理的问题 |
五、缓存优先缓存读多写少
不是所有接口都适合缓存。
适合缓存:
1 | 公开文章列表 |
不适合缓存:
1 | 支付创建 |
缓存前先问三个问题:
1 | 数据多久变化一次? |
如果答不上来,就先别缓存。
六、HTTP 缓存是最便宜的优化
公开 GET 接口可以先从 HTTP header 开始。
例如公开文章详情:
1 | app.get('/api/public/posts/:slug', async (c) => { |
这类缓存不用引入新组件。
浏览器、CDN、边缘缓存都能理解。
如果内容变化不频繁,再加 ETag:
1 | const etag = `"post-${post.id}-${post.updatedAt}"` |
很多接口根本不需要复杂缓存系统。
HTTP 缓存已经够用。
七、KV 缓存适合昂贵聚合
如果某个接口要查多张表、做聚合、再调第三方,可以用 KV 缓存结果。
示例:
1 | app.get('/api/dashboard', async (c) => { |
注意 key 要带版本:
1 | tenant:<tenantId>:dashboard:v1 |
以后结构变化,可以升级到 v2,不用担心旧缓存污染新代码。
八、缓存失效比缓存读取更难
缓存写进去很容易。
难的是它什么时候失效。
常见策略:
1 | 短 TTL |
早期项目我更推荐:
1 | 短 TTL + 写操作删除缓存 |
例如更新文章后:
1 | await updatePost(postId, input) |
不要一开始就设计复杂缓存一致性。
先让缓存最多旧几十秒。
这对大多数内容型 API 已经够用。
九、数据库优化通常更重要
如果接口慢在数据库,Hono 再快也救不了。
常见问题:
1 | 没有索引 |
多租户文章列表应该有类似索引:
1 | create index idx_posts_tenant_created |
查询只拿需要字段:
1 | select id, title, status, created_at |
不要接口只需要标题,却把 content 大字段也查出来。
很多性能问题就是这样一点点堆出来的。
十、分页不要无限 offset
普通后台列表用 offset 没问题。
但数据量大时,深分页会越来越慢:
1 | select * |
更适合 API 的方式是游标分页:
1 | app.get('/api/posts', async (c) => { |
游标可以是:
1 | created_at + id |
对移动端、无限滚动、时间线列表,游标分页更稳。
十一、运行时选择不是第一优化项
Hono 可以跑在很多运行时:
1 | Cloudflare Workers |
这很酷。
但性能问题不要一上来就怪运行时。
先确认:
1 | 慢请求耗时分布 |
如果用户都在中国大陆,而数据库在美国,换框架意义不大。
如果每个请求都查 20 次数据库,换 Bun 也解决不了根因。
运行时选择要服务于架构,不是替代架构。
十二、性能优化顺序
我推荐这个顺序:
1 | 1. 加请求耗时日志 |
不要反过来。
一上来就换平台、换数据库、换框架,很容易忙半天只优化了 5%。
Hono 的性能优势是它足够轻,不会成为主要瓶颈。
这反而要求你把注意力放回真正的系统瓶颈上。
快,不是 benchmark 上的数字。
快,是用户请求从进入系统到拿到结果的整个链路都没有浪费。