簡介
疑似資料庫中斷對所有人來說都可能是壓力很大的事件。使用者會感到沮喪並擔心他們的資料可能會遺失,開發人員會急忙尋找原因,而公司利害關係人則計算每一分鐘可能造成的收入損失。
在發生中斷事件時,讓資料庫恢復健康狀態是首要之務。然而,在如此緊張的事件中,甚至很難推斷問題可能出在哪裡。本文旨在引導您了解資料庫可能當機的一些最常見原因,以及您可以採取的修復措施。
本文涵蓋處理資料庫中斷時需要查看的各種領域,包括
問題 | 潛在問題 | 行動 |
---|---|---|
資料庫似乎不接受連線 | 資料庫使用者憑證問題、連線字串問題、達到連線限制 | 檢查資料庫伺服器日誌以判斷原因 |
網路通訊似乎受損 | 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 提供一個名為「日誌與查詢」的標籤,可直接從部署管理選單存取。
此區段包含幾種不同的記錄資訊,包括一般的「近期日誌」區段。您可能會發現連線到資料庫的問題或其他可能發生的問題的跡象。
網路問題
網路相關問題可能會導致您的資料庫無法使用或似乎當機。在由用戶端層、應用程式層(後端)和資料層(資料庫)組成的三層式應用程式中,網路問題可能會在這三層之間發生。
網路相關問題包括
- VPC 和防火牆原則問題
- 延遲和應用程式與資料庫之間的逾時
當您的資料庫似乎當機或沒有回應,並且您懷疑這可能與網路問題有關時,最好先判斷流量是否受到防火牆原則的封鎖,或者是否存在實際的網路問題。
注意:以下建議基於可能不適用於您的架構的假設。因此,建議您在偵錯資料庫問題時,了解失敗模式及其原因。
VPC
在雲端平台上佈建雲端資源(例如資料庫)時,它們會隔離在虛擬私有雲 (VPC) 中。實際上,VPC 作為應用程式資源的私有網路,並與公共網際網路隔離。如果您的資料庫和應用程式不在同一個 VPC(或同一個雲端)中,您通常需要設定 VPC 的防火牆規則,以允許應用程式的 IP 存取資料庫。
防火牆規則
當您將應用程式和資料庫部署到不同的雲端平台或不同的 VPC 時,您需要設定防火牆以允許從應用程式存取資料庫。
雖然某些防火牆原則使用黑洞原則來靜默丟棄不允許的流量,但其他原則會傳送拒絕回應,這可以協助您判斷防火牆是否是造成連線中斷的原因。同樣地,如果您遇到「沒有到主機的路徑」類型的錯誤,則可能表示網路區段已關閉,或路由邏輯不正確。
此外,如果您的應用程式的 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
指令之外,許多雲端資料庫供應商還提供自動視覺化造成問題的查詢的方法。此資訊通常可以在雲端供應商的「慢速查詢」檢視中找到。查看您的資料庫供應商的「慢速查詢」區段,以找出哪些查詢可能對您的應用程式產生不利影響。
新增索引
與大量資料相關的問題通常可以使用索引來解決。
資料庫表格的索引可以概念化為書籍中的索引。如果您在書中尋找有關特定主題的資訊,則需要很長時間才能讀完整本書才能找到它。相反地,您可以查閱索引來查找感興趣的特定主題。如果存在,它會將您指向書中討論該主題的特定頁面。
相同的概念適用於資料庫索引。根據常見的存取模式在資料庫表格中新增索引,可以實現快速查找。
例如,如果您有一個使用者資料庫表格,其中包含 email
欄位,並且查詢該表格以根據使用者的電子郵件尋找使用者,則根據 email
欄位為該表格建立索引有助於提高效能。如果沒有索引,將掃描整個表格以按電子郵件地址尋找使用者。如果表格大小非常大,這可能會造成嚴重的效能問題。但是,如果為該欄位建立索引,則查找速度會很快,因為搜尋特定電子郵件會立即指向所需的確切列。
在資料庫中新增索引需要規劃和考量,這應該在您的資料庫導致應用程式中斷之前完成。但是,如果您可以找到導致中斷的特定查詢,則新增索引以立即緩解壓力可能會有所幫助。
破壞性程式碼變更
疑似資料庫中斷可能可以追溯到最近對用戶端或伺服器程式碼所做的破壞性變更。在這些情況下,很可能不是資料庫本身發生中斷,而是用於檢索資料、處理資料並將其返回給用戶端的程式碼可能已損壞。
可能導致中斷的程式碼變更通常分為三類
- 資料庫查詢
- 伺服器程式碼
- 用戶端程式碼
資料庫查詢
即使對資料庫查詢進行微小的變更,也可能會影響應用程式。如果使用原始的 SQL,則查詢陳述式可能在最近的程式碼變更中已失效。如果查詢本身仍然有效,則可能已變更為傳回應用程式其他部分不再可處理的結果。
檢查原始碼控制變更,以尋找最近對資料庫存取 (SQL 陳述式或 ORM 使用) 所做的變更。嘗試隔離與受影響應用程式特定區域相關的查詢。
如果沒有資料庫存取變更的跡象,則另一種可能性是資料庫結構描述已變更,但尚未執行移轉。尋找任何可能仍需要應用於生產資料庫的最近移轉。
用戶端和伺服器程式碼
對用戶端或伺服器中應用程式程式碼的變更,即使是很小的變更,也可能造成看起來像資料庫中斷的情況。可能有很多特定問題是問題的根源,但一些範例包括
- 用戶端可能正在呼叫不再存在的伺服器端點
- 伺服器端點已變更酬載或查詢參數的驗證規則
- 伺服器正在嘗試從最近的結構描述變更中已失效的傳回資料中存取屬性
結論
資料庫中斷,無論根本原因為何,都可能是非常緊張的事件。在急於嘗試讓應用程式恢復運作時,可能難以正確地推理資料庫問題可能源自何處。
每個資料庫中斷都是獨一無二的,並且在發生中斷時沒有一體適用的解決方案。但是,精通可能的問題及其相關的解決方案可以幫助您更有效地確定問題的原因,並縮短恢復運作所需的時間。
與大多數問題解決一樣,最好在問題發生之前熟悉問題的潛在解決方案。我們希望本指南能幫助您實現這一目標。