博客

唤醒数据库时不再遇到失效连接

Mythex 上已发布的应用及其数据库,在无人使用时都会进入休眠。应用唤醒时,第一个请求可能会被卡住 10–12 秒,因为它尝试复用在一切休眠期间已经失效的数据库连接。现在,我们为 Spring Boot 应用配置了在休眠后依然安全的数据库设置,并要求智能体以同样的方式为任何技术栈构建应用。在托管平台上,唤醒后的第一个请求从 12.7 秒降到了 2.3 秒。

为什么应用和数据库会休眠

已发布的 Mythex 应用运行在一台空闲几分钟后就会挂起的机器上,而它的数据库是无服务器的 Postgres,空闲五分钟后休眠,并在下一次请求时唤醒。这正是让闲置应用保持低成本的原因:数据库按其实际使用量计费,而休眠的应用不消耗计算资源。我们在按实际使用量计费的数据库一文中介绍过计费方面的内容。

问题在于,两者各自休眠,而它们对「连接」的理解并不一致。

症状:第一次访问很慢

大多数服务器应用会保持一个连接池:一小批已打开、随时待用的数据库连接,这样每个请求就不必承担新建连接的开销。这对于一直在线的应用来说是正确的设计。

休眠时会发生以下情况:

  1. 应用正在运行,连接池中有几个空闲连接。
  2. 无人访问。应用所在的机器挂起——冻结,连接池保持原样。
  3. 数据库进入休眠,并断开它那一端的所有连接。
  4. 访客到来。应用原样唤醒,但仍然持有数据库已不再认可的连接。
  5. 连接池分配出其中一个连接。它必须先尝试并等待,才能发现它已经失效,然后放弃它。

在 Spring Boot 应用中,验证这些失效连接会把第一个请求阻塞约 10 秒,最后以「Connection is not available」失败。对访客来说,网站就像卡住了一样,然后可能显示一个错误。

为什么 Spring 应用受影响最大

Spring Boot 是一个流行的 Java 框架,默认使用名为 HikariCP 的连接池。Spring 可以通过设置来配置该连接池,包括保留多少个空闲连接。

但 Mythex 为每个应用提供数据库时,使用的是一个 DATABASE_URL 环境变量,格式为 postgresql://user:pass@host/db。Spring 无法直接读取这种格式。因此,在 Mythex 上构建的 Spring 应用都是通过解析这个 URL,在代码中自行构建连接池。而手动构建的连接池会忽略 Spring 所有的 spring.datasource.* 设置。即使有人知道正确的连接池设置,它们也无处生效。

修复第一部分:Spring 可以使用的设置

现在,只要项目有数据库,我们还会提供 Spring 原生读取的环境变量:

  • SPRING_DATASOURCE_URL、SPRING_DATASOURCE_USERNAME 和 SPRING_DATASOURCE_PASSWORD,由 DATABASE_URL 转换而来(除非 URL 另有说明,否则要求 SSL)。
  • 能在休眠后保持有效的连接池设置:
设置值原因
最小空闲连接数0请求之间不保留任何已打开的连接,因此没有可能失效的连接
空闲超时30 秒尽快关闭未使用的连接
最大生命周期2 分钟在任何连接变旧之前将其淘汰
连接超时5 秒如果数据库响应缓慢,不让请求长时间挂起

由于不保留空闲连接,唤醒后的应用只需打开一个全新的连接,而不必在失效连接中逐个尝试。

存在这些变量时,Spring 会自行配置连接池,并应用这些设置。项目自己设置的值始终优先;我们只补充缺失的部分。非 Spring 应用会忽略这些变量。

几乎让它失效的细节

Spring 将环境变量映射到其设置的规则很容易搞错。minimum-idle 这一设置必须写成 SPRING_DATASOURCE_HIKARI_MINIMUMIDLE——去掉连字符,单词连在一起。如果写成看起来更自然的 SPRING_DATASOURCE_HIKARI_MINIMUM_IDLE,它会映射到一个不同的、并不存在的设置(minimum.idle),并且会被悄悄忽略。

这类错误能通过每一次代码审查,却在生产环境中什么也不做。因此,我们编写了一个测试,既检查这些值是否已设置,也检查没有任何变量使用下划线分隔的形式。

修复第二部分:所有其他技术栈

Spring 并不是唯一带连接池的框架,而我们也无法为每种语言中的每个库预先配置。因此,Mythex 智能体——负责编写你的应用的 AI——现在针对任何技术栈都有一条常驻指令:已发布的应用及其数据库在空闲时会休眠,因此一个保持打开的连接,到下一次请求时可能已经失效。它应当:

  • 不保持任何空闲的数据库连接,或在使用前逐个检查;
  • 在连接错误时重试一次;以及
  • 使用平台在环境变量中提供的数据库设置,而不是通过解析 DATABASE_URL 自行构建连接池。

最后一点与第一部分形成闭环:使用平台提供的设置构建的应用,会自动获得能够抵御休眠的配置。

结果

在我们运行已发布应用的托管平台上测量,唤醒后的第一个请求从 12.7 秒降到了 2.3 秒。

我们的收获

  • 每个会休眠的层都需要就「什么能保留下来」达成一致。 冻结的应用和休眠的数据库各自做出了合理的选择。合在一起,应用醒来时持有的状态已经不再是真实的。
  • 为常驻服务器设计的默认值,不适合会缩容到零的应用。 保持连接温热是个好主意,但前提是它周围的一切都没有变冷。
  • 以框架自己的格式提供数据。 对我们来说,一个 URL 很简单,却迫使每个 Spring 应用都要手工构建一个忽略自身配置的连接池。
  • 测试设置的拼写是否与框架的读取方式一致。 名称错误的正确值什么也不做,而且没有任何提示。

如果你刚开始了解 Mythex 上的数据库工作方式,请参阅数据库文档,以及我们关于什么是 Postgres的解释文章。

Mythex · Pricing · 用 AI 构建任何东西 · 网站和应用模板 · Mythex 对比 · AI 应用构建术语表 · 免费的 AI 应用构建工具 · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 繁體中文(香港) 繁體中文(台灣)