一个简单的NestJS工作流,在Google Cloud Run后台运行时,偶尔要等11秒才能继续执行——而真正打中数据库的SQL查询,最快的只花了42毫秒。
问题最初暴露得很模糊:BullMQ定时器触发后,工作负载需要几秒到十几秒才能恢复。期望值是数百毫秒,实际延迟却呈现出双峰分布。大部分恢复缓慢但还能忍,少数请求直接卡死10秒以上。
直觉会立刻指向数据库。PostgreSQL?慢查询?索引缺失?但加了一圈计时日志后,整个执行链路上真正可疑的点反而开始后移。
计时覆盖了从定时器触发、Redis传递、工作流服务,到TypeORM仓库访问Cloud SQL的每个环节。结果很明确:瓶颈集中在持久化层,而且延迟方差极大。更蹊跷的是,同样的代码在本地跑,完全没有延迟。这意味着仓库实现、工作流引擎、BullMQ和Redis都不是元凶,嫌疑迅速转移到云基础设施——Cloud Run配置、PostgreSQL连接池、Cloud SQL实例和它背后的网络。
Cloud SQL的查询洞察平台给出了一个反直觉的信号。所有相关SQL的执行时间都落在42到73毫秒之间,平均不到70毫秒。如果数据库本身跑得飞快,那多余的10秒就必然消耗在“请求到达数据库之前”。
于是埋点不再观测黑盒的repository.findById,而是直接入侵到TypeORM的QueryRunner。并且把复杂SQL替换成最简单的SELECT 1,彻底撇开索引、JOIN和查询计划的影响。这一次,真相浮出水面且呈现出两种泾渭不同的模式。
最佳情况下,连接获取耗时2毫秒,查询执行耗时0毫秒,一切正常。但最差的一种模式是这样的:获取数据库连接花费2293毫秒,而执行SELECT 1只花了2毫秒。近2.3秒的总延迟里,99.9%的时间都消耗在“拿到连接”这一步上。
到这里,真正的调查方向才被扭转。问题不在PostgreSQL,不在SQL,不在ORM,甚至不在Cloud SQL的查询能力——瓶颈出在连接到达数据库之前的那段网络或连接池配置上。后续对Cloud Run和Cloud SQL之间握手的进一步排查,最终锁定了具体的延迟来源。
整个过程留下了一条清晰的调试方法论:与其猜测黑盒里的瓶颈,不如一层层插入毫秒级计时代码,把每个环节都拆成可以单独度量的原子步骤。当数据告诉你“连接获取2293毫秒,查询2毫秒”时,任何主观猜测都会自动失效。
热门跟贴