網誌

喚醒資料庫時不再遇到失效連線

在 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 代理,現在對任何技術棧都有一條常設指示:已發佈的應用程式及其資料庫在閒置時會休眠,所以保持開啟的連線在下一個請求時可能已經失效。它應該:

  • 不保留任何閒置的資料庫連線,或在使用前逐一檢查,
  • 遇到連線錯誤時重試一次,以及
  • 使用平台在環境中提供的資料庫設定,而不是自行解析 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 简体中文 繁體中文(台灣)