内容提要
本文介绍如何让 Databricks 应用在用户增长时保持快速可靠,核心是按设计、构建、测试部署、运维四个阶段进行扩展:先设定延迟与峰值负载目标,将重负载交给专门服务,应用只负责 UI 和协调;采用异步代码,将会话状态分为可重建的软状态和持久化的硬状态;通过负载测试和遥测数据决定纵向扩容(更大实例)还是横向扩容(更多实例),而非盲目增加算力。
延伸解读
先定目标再选架构
文章强调扩展决策应在写代码前开始:先与用户确认延迟和峰值负载目标,再决定工作负载放在哪里、数据存在哪里。通过梳理用户旅程,估算每个动作调用的服务、调用次数、依赖关系、完成时间和缓存可容忍的陈旧度,这些假设会直接影响架构和首批测试目标。原型跑起来后,再用真实调用数据修正估算,避免凭直觉堆算力。
重活外移,应用只做UI和协调
Databricks应用的计算资源应主要用于服务UI和协调调用,而非承担重负载。文章建议将每类工作交给专门服务:长耗时调用放到后台任务,UI轮询状态或接收流式更新;不受控的代码(如用户编写或代理生成的代码)放到Databricks Sandbox隔离环境运行,避免其CPU和内存占用影响服务用户的实例。这样应用本身的扩展瓶颈通常变成并发请求处理能力。
软硬状态分离保障会话可恢复
多数应用会在内存中保留会话状态,这本身没问题,但需要明确哪些可以丢失。文章建议将状态分为软状态(可重建,如本地缓存)和硬状态(需持久化到Lakebase或Unity Catalog表)。Databricks Apps尽力保持会话粘性,但重启、故障和扩缩容仍可能迁移会话,因此任何实例都必须能从硬状态重建会话。部署时SIGTERM后只有15秒窗口,重要工作应在正常运行时已保存。
用负载测试和遥测决定扩容方向
纵向扩容(更大实例)适合需要更多内存或能利用额外核心跑更多worker的场景;横向扩容(更多实例)适合请求突发或单进程受限于单核的场景。文章建议从Medium起步,用负载测试逐步增加并发用户,观察吞吐何时停止增长或延迟、错误率突破目标,同时查看系统指标判断先触达哪个限制。生产环境保持遥测开启,通过端到端追踪定位慢在SQL、模型调用还是连接池,再决定加应用算力还是扩后端服务。
Q&A
Databricks 应用扩展应该按哪几个阶段进行?
按四个阶段:设计、构建、测试与部署、运维。设计阶段设定延迟和峰值负载目标;构建阶段采用异步代码并分离软硬状态;测试部署阶段通过负载测试决定实例大小和数量;运维阶段持续用遥测定位瓶颈并调整。
什么时候该纵向扩容(更大实例),什么时候该横向扩容(更多实例)?
取决于应用先碰到哪个限制。如果请求突发或单进程应用受限于单核,应横向扩容增加实例;如果应用需要更多内存(如缓存大型数据集或模型),或运行多个 worker 能利用额外核心,应纵向扩容到 Large 或 XLarge。
如何保证用户会话在应用重启或扩缩容后不丢失?
将会话状态分为软状态(可重建)和硬状态(持久化到 Lakebase 或 Unity Catalog 表)。Databricks Apps 尽力保持会话亲和性,但重启或扩缩容仍可能迁移会话,因此任何实例都必须能从硬状态重建会话。
Databricks Apps 提供哪些实例规格?应该从哪个开始?
提供 Medium(默认,最多 2 vCPU、6 GB)、Large(最多 4 vCPU、12 GB)和 XLarge(最多 12 vCPU、48 GB)。建议从 Medium 开始,通过负载测试确定实际需要的规格。
扩展后的应用最多能打开多少个 Lakebase 连接?如何估算?
每个 worker 一个连接池时,上限为实例数 × 每实例 worker 数 × 池最大连接数。例如 5 个实例、每实例 8 个 worker、池最大 10,则最多 400 个连接。需与数据库文档限制比较,并为部署留余量。
如何通过负载测试决定实例大小和 worker 数量?
使用真实用户旅程在预期峰值下进行负载测试,逐步增加并发用户,直到吞吐量不再增长或延迟/错误率超标。同时观察系统指标(请求率、延迟、CPU/内存),尝试不同实例大小和 worker 数,因为更大配置不一定更快。
为什么 Databricks 应用推荐使用异步代码?
大多数应用时间花在等待其他服务(SQL、模型调用、API)。阻塞调用会占用执行槽,少量慢调用就可能耗尽容量。异步处理程序在等待非阻塞调用时可继续服务其他请求,避免请求排队。