簡介
疑似資料庫中斷事件對每個人來說都可能是一個充滿壓力的時刻。使用者會感到沮喪並擔心他們的資料可能會丟失,開發人員會爭先恐後地尋找原因,而公司利益相關者則會計算每分鐘可能造成的收入損失。
在資料庫中斷事件中,將資料庫恢復到健康狀態是首要任務。然而,在如此緊張的事件中,甚至很難推斷出問題可能是什麼。本文旨在幫助您瞭解資料庫可能宕機的一些最常見原因以及您可以採取哪些措施來修復它。
本文涵蓋了在處理資料庫中斷時需要注意的各個方面,包括:
| 問題 | 潛在問題 | 操作 |
|---|---|---|
| 資料庫似乎不接受連線 | 資料庫使用者憑據問題、連線字串問題、連線數達到限制 | 檢查資料庫伺服器日誌以確定原因 |
| 網路通訊似乎受損 | VPC 和防火牆問題,應用程式與資料庫之間的延遲和超時 | 檢查防火牆規則,查詢延遲和超時,檢查是否達到連線限制 |
| 資料庫伺服器或應用伺服器崩潰 | 潛在的資料量問題導致資料庫伺服器和應用伺服器達到資源限制 | 查詢大量返回資料,最佳化查詢,新增索引 |
| 資料庫伺服器正常執行但應用程式不顯示資料 | 潛在的近期程式碼更改,包括模式更改、未應用的遷移、格式錯誤的查詢 | 檢查原始碼管理歷史以查詢近期更改,並在必要時回滾 |
閱讀本文的最佳時機是在資料庫宕機之前。如果您的資料庫已經宕機,那麼次佳時機就是現在。
資料庫連線問題
應用日誌中的線索
如果您的資料庫似乎已宕機,您的應用程式日誌可能會為您提供有關接受請求或連線資料庫時出現任何問題的見解。由於應用伺服器處理客戶端請求和資料庫請求,如果資料庫出現問題,它通常會記錄錯誤。
如果您正在使用 Prisma Client,您可以配置日誌來控制日誌的生成方式。
應用伺服器是否成功處理連線?
一個暴露 API 並透過查詢資料庫來提供請求的應用程式伺服器有兩種連線:
- 使用者嚮應用伺服器暴露的 API 傳送請求
- 應用伺服器向資料庫傳送請求
在除錯中斷問題時,請記住這兩種連線都可能導致中斷。
應用伺服器框架通常內建日誌記錄器。在伺服器啟動後記錄伺服器可以接受連線是一種常見做法。
例如,看看這條日誌行:
{"level":30,"time":1617808854673,"pid":96741,"hostname":"do-server-1","msg":"Server listening at http://0.0.0.0:8000"}
日誌顯示伺服器已啟動並在埠 8000 接受連線。但這不足以確定自伺服器啟動以來是否發生了錯誤。
下一步是檢視最新日誌並檢查最近對伺服器請求的 HTTP 狀態碼。通常請求日誌如下所示:
{"level":30,"time":1617809865718,"pid":97326,"hostname":"do-server-1","reqId":5,"req":{"method":"POST","url":"/graphql","hostname":"0.0.0.0:3000","remoteAddress":"127.0.0.1","remotePort":53540},"msg":"incoming request"}{"level":30,"time":1617809865719,"pid":97326,"hostname":"do-server-1","reqId":5,"res":{"statusCode":200},"responseTime":1.1810400485992432,"msg":"request completed"}
每個傳入的請求日誌都包含時間戳、URL 和請求的其他資訊。第一個日誌表示有請求進入,而第二個日誌顯示請求成功,HTTP 狀態碼為 200。
最近是否有資料庫錯誤?
由於您正在嘗試除錯與資料庫相關的問題,因此您應該特別查詢失敗的請求。您可以透過過濾響應狀態碼為 5xx(例如 500)的請求來找到這些請求,這表示伺服器錯誤。
如果您發現多個狀態碼為 500 的請求,請查詢這些請求開始的時間點,並檢查是否有其他已記錄的錯誤。
如果資料庫宕機或資料庫憑據錯誤,您可以在日誌中看到。例如:
Can't reach database server at `db-postgresql-563564.b.db.ondigitalocean.com`:`5432`
上述日誌表明您的應用伺服器無法連線到資料庫。在這種情況下,請檢查應用伺服器中的資料庫 URL。
如果您正在使用 Prisma Client,錯誤程式碼參考可以幫助您診斷錯誤含義以及如何修復它們。
資料庫問題排查
既然您知道您的應用伺服器正在執行但無法連線到資料庫,下一步就是確定原因。最好的方法是排除問題的潛在來源。
如果您正在使用資料庫的託管服務,請首先檢查您的雲服務提供商的狀態頁面。大多數雲服務提供商都提供一種方法來檢查其基礎設施是否正常執行,例如 Digital Ocean、Google Cloud Platform 和 AWS。
一旦您排除了雲平臺的問題,接下來要檢查的是您的資料庫例項的日誌或狀態。
託管資料庫日誌
如果您正在使用的託管資料庫提供商似乎執行正常,那麼下一步就是檢查您的特定資料庫例項的日誌。
大多數雲平臺除了吞吐量和查詢統計等指標外,還提供檢視資料庫日誌的方式。日誌的佈局和詳細程度因雲提供商而異。然而,大多數雲提供商都提供了足夠詳細的資訊來幫助您查詢問題。
每個雲提供商訪問資料庫日誌的方式也不同。大多數都可以在部署管理頁面輕鬆訪問日誌。
例如,DigitalOcean 提供一個名為“日誌與查詢 (Logs & Queries)”的選項卡,可直接從部署管理選單訪問。
本節包含多種不同的日誌資訊,包括一個通用的“近期日誌 (Recent Logs)”部分。您可能會發現連線資料庫問題或其他可能發生的跡象。
網路問題
網路相關問題可能導致您的資料庫不可用或看似宕機。在由客戶端層、應用層(後端)和資料層(資料庫)組成的三層應用程式中,網路問題可能出現在這三層之間。
網路相關問題包括:
- VPC 和防火牆策略問題
- 應用程式與資料庫之間的延遲和超時
當您的資料庫似乎宕機或無響應,並且您懷疑這可能與網路問題有關時,最好首先確定流量是被防火牆策略阻塞還是存在實際的網路問題。
注意:以下建議基於可能不適用於您架構的假設。因此,在除錯資料庫問題時,建議您瞭解故障模式及其原因。
VPC
在雲平臺上配置資料庫等雲資源時,它們被隔離在虛擬私有云 (VPC) 中。實際上,VPC 充當您應用程式資源的私有網路,並與公共網際網路隔離。如果您的資料庫和應用程式不在同一個 VPC(或同一個雲)中,您通常需要配置 VPC 的防火牆規則,以允許應用程式的 IP 訪問資料庫。
防火牆規則
當您將應用程式和資料庫部署到不同的雲平臺或不同的 VPC 時,您需要配置防火牆以允許從應用程式訪問資料庫。
雖然某些防火牆策略使用*黑洞*策略靜默丟棄不允許的流量,但其他策略會發送拒絕響應,這可以幫助您確定防火牆是否是導致連線中斷的原因。同樣,如果您遇到“無主機路由 (no route to host)”型別的錯誤,這可能意味著網路段已宕機或路由邏輯不正確。
此外,如果您的應用程式 IP 是動態的,它隨時可能更改。當應用程式的 IP 更改時,它可能無法連線到資料庫,直到防火牆更新以允許新的 IP 訪問。
防火牆規則補救措施
通常最好將應用程式和資料庫部署在同一個 VPC 和同一個區域中,以便兩者透過私有網路進行通訊。這還可以防止公共網路可能導致的瓶頸。但是,如果無法實現,則有幾種可能的修復方法。
第一種方法是為您的應用程式使用專用 IP,並新增一條防火牆規則,允許該 IP 的訪問。這種方法確保您不需要更新防火牆規則,因為 IP 保持不變。
如果由於雲平臺限制或部署方法(例如無伺服器)而無法使用專用 IP,請考慮透過公共網路啟用對資料庫的訪問。雖然這種方法會降低資料庫的安全性,但它能確保您的應用程式在您更新防火牆規則以適應應用程式 IP 更改之前不會遇到任何突然的停機。此外,您的資料庫的認證機制仍然是主要的防禦措施。
請注意,不建議從公共網際網路開啟到資料庫的連線。在可能的情況下,我們建議將連線限制到特定的 IP 地址或使用 VPC 對等連線等技術。
延遲和超時
當應用程式和資料庫之間的地理和網路距離較大時,請求延遲增加和超時錯誤可能會成為問題。在這種情況下,可能需要在應用程式中增加資料庫連線超時時間以避免超時錯誤。
ORM 和 查詢構建器維護著到資料庫的連線池。這些連線通常具有連線超時配置,用於控制建立連線時在超時前等待多長時間。
建議設定初始基線超時值並對應用程式進行負載測試以檢視其效能。根據負載測試中失敗請求的數量,您可能需要調整超時並再次嘗試,直到您確信超時不是問題的原因。如果您的連線超時配置過低,則存在這些超時導致請求失敗的風險。
連線數耗盡
基於連線的資料庫(如 MySQL 和 PostgreSQL)的另一個常見挑戰是您可能會很快耗盡資料庫的連線限制。面向連線的資料庫對到資料庫的開放連線數施加了限制。
當您使用傳統的長執行程序模型部署應用程式時,您可以在連線池中將許多傳入請求複用到更少的資料庫連線上。例如,單個伺服器例項可能使用 20 個數據庫連線池處理 100 個併發 HTTP 請求。
然而,在無伺服器函式上,每個函式例項一次只能處理 1 個 HTTP 請求。由於每個傳入請求至少需要一個到資料庫的連線,因此您無法複用資料庫連線。
鑑於此,建議使用資料庫連線限制,然後以不會耗盡資料庫連線限制的方式在連線到它的伺服器例項之間分配這些連線。例如,假設您的資料庫連線限制為 20。如果您有兩個應用程式伺服器例項,您會希望將每個伺服器例項的連線池設定為最多 10 個連線。
對於無伺服器部署,考慮執行一個外部連線池,如 PgBouncer——這是一個額外的基礎設施元件,它維護著到資料庫的連線,並複用來自無伺服器函式的傳入資料庫查詢。
資料量問題
隨著應用程式的增長,其資料量也很可能會隨之增長。效能和資料庫正常執行時間的一個關鍵考慮因素是為滿足給定請求而處理的資料量。當總資料量較小時為應用程式編寫的低效查詢,在資料量增長時可能會變成瓶頸,從而導致效能急劇下降並引發中斷。
大量資料可能在三個位置產生影響:
- 資料庫伺服器
- 應用伺服器
- 客戶端
資料庫伺服器
當應用伺服器發出資料庫查詢時,資料庫伺服器的職責是檢索請求的資料並將其傳送回應用伺服器。在此過程中,資料庫伺服器必須首先將檢索到的資料掃描到其自身記憶體中。當資料量較小時,將資料掃描到記憶體並轉發到應用伺服器的過程微不足道。然而,如果查詢返回的資料量非常大,配置不足的資料庫伺服器可能會在負載下崩潰。
當最初在資料庫總規模較小時足夠使用的查詢,隨著資料規模的增長而未得到充分最佳化時,這種情況經常發生。
考慮一個場景,應用程式需要為使用者顯示聯絡人列表。用於提供此資料的典型資料庫查詢可能會選擇限於該使用者的資料。
SELECT * from contacts WHERE userId = 123;
根據應用程式的需求,應用伺服器可能除了將結果轉發給客戶端外,無需執行其他操作。此查詢不太可能導致資料量問題。
然而,考慮一下如果上述查詢中沒有 WHERE 子句會發生什麼。
SELECT * from contacts;
如果查詢改為請求資料庫中所有聯絡人,然後由應用伺服器負責在將結果轉發到客戶端之前根據 userId 過濾列表,那麼資料量的差異將是天文數字。
在總體資料量相對較小的情況下,這種不完善的查詢可能不會被發現。隨著資料量的增長,效能下降可能會變得明顯,但可能不會完全破壞應用程式。然而,隨著這種情況的發生,資料庫伺服器將超出其可能配置的負載。隨著資料量進一步增長,可能會達到一個臨界點,資料庫伺服器將無法再處理負載。
應用伺服器
正如資料庫伺服器處理大量資料的能力並非無限一樣,應用伺服器也同樣如此。您可能對應用伺服器的容量和規模有比資料庫伺服器更多的控制,但增加應用伺服器的容量可能沒有必要。
從資料庫伺服器返回的大量資料需要在應用伺服器上花費時間和資源進行處理。如果應用伺服器正在響應來自客戶端應用程式的請求,那麼在應用伺服器上的長時間等待可能會導致問題。許多雲託管提供商會在幾分鐘後強制執行請求超時。Web 瀏覽器會在請求處於“待處理”狀態幾分鐘後自動重試請求,這會進一步給系統帶來壓力。
客戶端
客戶端應用程式可能最容易受到大量資料量導致的瓶頸影響。與應用伺服器和資料庫伺服器不同(您可能能夠增加其容量),在瀏覽器或移動裝置上執行的客戶端應用程式受限於瀏覽器、作業系統或兩者的限制。
向客戶端應用程式傳送過大的資料量將導致處理延遲,並可能嚴重降低響應速度。一旦達到給定資料大小,瀏覽器可能會完全無響應並最終崩潰。
如果非常大的資料量從資料庫一直轉發到客戶端,那麼在整個過程中都會感受到效能下降。這種三重效應將導致漫長的載入時間,並可能在這些點中的任何一個地方出現感知到的中斷。使問題複雜化的是,排查瓶頸所在變得困難。
資料量補救措施
解決資料量問題導致的效能下降和中斷的辦法幾乎總是限制從資料庫伺服器返回的資料量。這樣做將緩解資料庫伺服器、應用伺服器和客戶端的問題。
使用 WHERE 子句限制資料範圍
在上述虛構的查詢示例中,查詢了資料庫的整個聯絡人表。這意味著資料庫伺服器需要將整個表掃描到記憶體中,然後才過濾到所需的結果,這會給資料庫伺服器和應用伺服器帶來巨大的負載。
首先查詢範圍過大的查詢。很可能這些查詢在資料庫伺服器上處理時間很長。檢視長時間執行查詢的日誌可以指示這種情況發生在哪裡。與其返回大量結果然後用應用程式程式碼過濾它們,不如構建您的查詢以返回真正需要的結果子集。在 SQL 中,使用 WHERE 子句來精確獲取您實際需要的資料。
使用 LIMIT 和 OFFSET 分頁資料
如果客戶端應用程式確實需要大量資料,建議引入分頁。
分頁是一種設計模式,它限制在給定時間內查詢和返回的記錄總數。如果使用者希望檢視更多記錄,他們必須明確請求。
這種設計模式最常使用 LIMIT 和 OFFSET 子句來實現。透過選擇跳過一定數量的記錄並限制返回結果的數量,客戶端和應用伺服器可以實現分頁體驗。
第一頁的查詢可能如下所示:
SELECT * from contacts WHERE userId = 123 LIMIT 25 OFFSET 0;
第二頁的查詢只需將 LIMIT 的值設定為 OFFSET 的值即可獲取下一“頁”。
SELECT * from contacts WHERE userId = 123 LIMIT 25 OFFSET 25;
排查資料量問題
大多數資料庫都提供了可以幫助排查資料量問題的工具。特別是,許多資料庫提供了 EXPLAIN 命令,它將顯示查詢的執行計劃並提供對任何相關問題的見解。
例如,在 PostgreSQL 中,EXPLAIN 命令顯示所考慮語句的查詢計劃,並將顯示估計的執行成本。這是查詢執行所需時間的估計值。
這些資訊有助於縮小範圍,找出導致瓶頸的具體表或查詢語句。
除了手動呼叫 EXPLAIN 命令外,許多雲資料庫提供商還提供一種自動視覺化導致問題的查詢的方法。這些資訊通常可以在雲提供商的“慢查詢 (Slow Queries)”檢視中找到。檢查您的資料庫提供商的慢查詢部分,以找出哪些查詢可能對您的應用程式產生不利影響。
新增索引
與大資料量相關的問題通常可以使用索引來解決。
資料庫表的索引可以像書的索引一樣理解。如果您想在一本書中查詢特定主題的資訊,通讀整本書來找到它將花費很長時間。相反,您可以查閱索引來查詢感興趣的特定主題。如果存在,它將指向書中討論該主題的具體頁面。
相同的概念也適用於資料庫索引。根據常見的訪問模式為資料庫表新增索引可以實現快速查詢。
例如,如果您有一個使用者資料庫表,其中包含一個 email 列,並且該表根據使用者的電子郵件進行查詢,那麼基於 email 列為該表建立索引有助於提高效能。如果沒有索引,將掃描整個表以透過電子郵件地址查詢使用者。如果表大小非常大,這可能會導致嚴重的效能問題。然而,如果為該列建立了索引,則查詢將非常快速,因為搜尋特定電子郵件將立即指向所需的精確行。
向資料庫新增索引需要規劃和考慮,並且應該在您的資料庫導致應用程式中斷之前完成。但是,如果您能找到導致中斷的特定查詢,則立即新增索引可能會有所幫助以減輕壓力。
破壞性程式碼變更
疑似資料庫中斷可能追溯到最近對客戶端或伺服器程式碼進行的破壞性更改。在這種情況下,很可能不是資料庫本身發生了中斷,而是用於檢索資料、處理資料並將其返回給客戶端的程式碼可能已損壞。
可能導致中斷的程式碼更改通常分為三類:
- 資料庫查詢
- 伺服器程式碼
- 客戶端程式碼
資料庫查詢
對資料庫查詢的更改,即使很小,也可能影響應用程式。如果使用原始 SQL,查詢語句可能在最近的程式碼更改中變得無效。如果查詢本身仍然有效,它可能已被更改為返回應用程式其他部分無法再處理的結果。
檢查原始碼控制更改,查詢資料庫訪問(SQL 語句或 ORM 使用)的近期更改。嘗試隔離與受影響應用程式特定區域相關的查詢。
如果沒有跡象表明資料庫訪問發生了變化,另一種可能性是資料庫模式已更改,但尚未執行遷移。查詢可能仍需要應用於生產資料庫的任何近期遷移。
客戶端和伺服器程式碼
對客戶端或伺服器應用程式程式碼的更改,即使很小,也可能導致看起來像是資料庫中斷的問題。導致此問題的具體問題有很多,但一些示例包括:
- 客戶端可能正在呼叫不再存在的伺服器端點
- 伺服器端點更改了有效負載或查詢引數的驗證規則
- 伺服器正在嘗試訪問因近期模式更改而失效的返回資料中的屬性
結論
資料庫中斷,無論其根本原因是什麼,都可能是令人非常緊張的事件。在急於讓應用程式恢復執行的過程中,可能很難正確地推斷資料庫問題可能源於何處。
每一次資料庫中斷都是獨一無二的,發生中斷時沒有一勞永逸的解決方案。然而,熟悉可能的問題及其相關解決方案可以幫助您更有效地確定問題原因,並縮短恢復執行所需的時間。
與大多數解決問題的方法一樣,最好在問題發生之前熟悉其潛在解決方案。我們希望本指南能幫助您實現這一目標。



