你需要了解的5種數據庫擴展解決方案
如果您的應用程序遇到負載問題,請抽出香檳! 您的網絡應用必須相當成功才能進入這一階段。 您已經達到了應用程序可以處理的用戶數量,事情開始變慢并且出錯。 網絡請求開始超時,數據庫查詢執行需要一段時間,頁面加載緩慢。 恭喜!您的應用已準備好進行擴展! 但是,該放下香檳了……您需要處理這些不斷增長的痛苦,直到用戶離開您的應用程序為止,否則競爭對手就會復制您的想法。
擴展成本
在垂直,水平和由內而外*分割數據庫之前,應牢記一個重要原則。 您不應該實施過早的優化或嘗試在實際需要時擴展應用程序。 實施伸縮解決方案帶來以下復雜性:
- 添加新功能需要更長的時間
- 系統變得越來越復雜,涉及更多的零件和變量
- 代碼可能更難測試
- 查找和解決錯誤變得更加困難
僅當您的應用程序處于滿負荷狀態時,才應接受這些折衷。 保持系統簡單,除非有保證,否則不要引入擴展復雜性。
由內而外的數據庫分片不是真正的解決方案。 關鍵是有各種各樣的擴展解決方案,除非需要,否則不要實施它們!
使用指標查找瓶頸
每個應用程序/系統都不同,要確定實施哪種擴展解決方案,您必須首先確定瓶頸在哪里。 是時候檢查您的資源監視系統,或者如果尚未創建一個。 無論您使用的堆棧如何,都有可用的工具來監視您的資源。 如果您在任何領先的IaaS(基礎設施即服務)提供商(例如AWS,Microsoft Azure和GCP)上運行,都有出色的應用程序性能管理工具可供選擇。
這些工具通過圖形和其他數據可視化方法說明了資源的性能。 使用這些圖形來查找峰值或平坦的頂部。 這些通常意味著資源不堪重負或已滿負荷,并且無法處理新的操作。 如果顯然沒有任何容量,但是您的應用程序運行緩慢,請嘗試在頻繁使用的操作中分散日志。 檢查日志中是否需要花費很長時間才能通過網絡加載資源,可能是其他服務器(例如第三方API)或您的數據庫服務器引入了延遲。 您應該將數據庫托管在另一臺服務器上,如果是這種情況,那么還應該檢查該計算機的資源監控。
通過考慮用戶如何使用您的應用程序,以及從邏輯上考慮開始顯示的錯誤或裂縫,確定瓶頸在哪里很簡單。 以Twitter為例,這個特定的平臺主要用于讀取和編寫推文。 如果Twitter的監視服務表明與這些操作有關的數據庫負擔沉重,則對他們的團隊來說,開始優化平臺的這一領域是有意義的。 在本文中,我們將深入研究數據庫擴展解決方案,這通常是失敗的第一點。 如果您還不熟悉系統設計,請閱讀簡短的文章,向您介紹該主題。 我建議在實施擴展解決方案之前對系統設計有所了解。

從鳥瞰視角擴展應用程序
現在我們對問題/瓶頸所在/位置有一個很好的認識,我們可以開始實施解決方案以解決這些問題。 記住,簡單是關鍵,我們要始終避免引入不必要的復雜性。
擴展解決方案的高層目標是使堆棧減少應用程序最常見請求的工作,或有效地將無法消除的工作負載分配到多個資源中。 縮放技術執行此操作的方式通常會轉換為以下一項或多項:
- 重用應用程序已查找的數據
- 消除了客戶端對應用程序已經擁有的數據的請求
- 存儲常見操作的結果,以減少重復計算
- 在請求-響應周期中避免復雜的操作
許多擴展技術可以歸結為某種形式的緩存。 過去,記憶是昂貴而稀缺的。 如今,將其添加到服務器已經很便宜。 與磁盤或網絡相比,內存訪問數據的速度要快許多個數量級。 在這個用戶有太多選擇的時代,再加上我們的關注度極低,速度和性能對于您的應用程序的生存至關重要。
數據庫擴展解決方案
1. 緩存數據庫查詢
緩存數據庫查詢是可以處理數據庫負載的最簡單的改進之一。通常,應用程序將包含少數查詢,這些查詢構成了大多數請求。不必每次都在網絡上對該數據進行往返,而是可以將其簡單地緩存在Web服務器的內存中。第一個請求將從數據庫中獲取數據,并將結果緩存在服務器上,以后的請求將從緩存中讀取。由于數據花費在網絡上的時間更少,并且距離客戶端更近,因此可以提高性能。
由于大量的工作負載分配給了緩存系統,因此還導致更多的數據庫服務器資源可用。除了提高可用性之外,如果數據庫不可用,則高速緩存仍可以為應用程序提供連續服務,從而使系統對故障的恢復能力更強。您可以使用許多工具對數據庫查詢日志進行分析,因此您可以查看哪些查詢花費的時間最長,哪些查詢運行的頻率最高。
顯然,緩存的數據會很快變得"陳舊"或過時。 您將必須選擇要緩存的數據以及要保留多長時間。 例如,在線報紙每24小時就會有一份新的日報,而不是每次用戶訪問該網站時都從數據庫中請求該數據,而是可以將這些數據在Web服務器上緩存24小時并直接從服務器提供該數據。 。 產品或業務要求將決定哪些內容可以緩存,哪些內容不能緩存。
2. 數據庫索引
數據庫索引是一種提高數據庫表上數據檢索操作速度的技術。 索引用于快速定位數據,而不必每次訪問表時都在表中搜索每一行。 通常,數據庫索引的數據結構將是二進制搜索樹。 這允許將訪問數據的時間復雜度從線性時間O(n)降低到對數時間Olog(n)。
根據表中的行數,這可以節省大量使用索引列的查詢的時間。 例如,如果您有10,000個用戶,并且您的應用程序的配置文件頁面按用戶名查找用戶,則未編制索引的查詢將檢查users表中的每一行,直到找到與傳遞給查詢的用戶名匹配的配置文件 。 這可能需要多達10,000個行檢查O(n)。 通過為"用戶名"列創建索引,數據庫可以在對數時間復雜度(Olog(n))下提取該行。 在這種情況下,行檢查的最大數量將是14,而不是10,000!
有效的索引編制通過提高效率來減輕數據庫的負載,這還可以顯著提高性能,從而帶來更好的用戶體驗。 創建索引確實會添加另一組要存儲在數據庫中的數據,因此在確定要索引的字段時必須謹慎判斷。 即使使用了現有的存儲空間,索引也還是很值得的,尤其是在現代開發中,內存便宜且性能是生存不可或缺的一部分。
在本節中略微提到了時間復雜度和數據結構,但沒有進行詳盡的解釋。 如果您有興趣學習或希望對時間復雜性和數據結構有所了解,那么上面鏈接的文章非常有用!
3. 會話存儲
許多應用程序通過將會話ID存儲在cookie中,然后將每個會話的鍵/值對的實際數據存儲在數據庫表中來處理會話。 這可能會成為對數據庫的大量讀取和寫入。 如果會話數據使數據庫不堪重負,那么最好重新考慮如何以及在何處存儲該數據。
將會話數據移動到內存緩存工具(例如redis或memcached)可能是一個不錯的選擇。 由于內存中的內存比大多數數據庫使用的持久性磁盤存儲要快,因此這將減輕數據庫中會話數據的負擔,并提高訪問速度。 但是,由于內存是易失性內存,因此如果緩存系統脫機,則存在丟失所有會話數據的風險。
您也可以考慮將會話實現更改為將會話信息存儲在cookie本身中,這將使您保持會話狀態的方法從服務器移到客戶端。 JWT是這種模式最流行的實現。 這將減輕數據庫中所有會話數據的負擔,并消除服務器端會話的依賴性,盡管這會帶來一系列挑戰。
4. 從站主復制
如果即使在緩存通用查詢,創建有效索引以及處理會話存儲之后,數據庫仍然承受著來自讀取的過多負載,那么復制可能是下一個最佳選擇。
使用從屬主復制,您只有一個數據庫可以寫入。 它被克隆到您讀取的幾個(根據需要)從數據庫中,每個從數據庫都位于另一臺計算機上(請參見下圖)。 這樣可以減輕主數據庫的讀取負擔,并將其分配到多個服務器上。 該模型還提高了寫操作的性能,因為主設備專門用于寫操作,同時由于從設備分布在不同區域,因此可以顯著提高讀取速度并減少延遲。

由于每個從數據庫都在另一臺計算機上,因此對主數據庫的寫入需要傳播到從數據庫,這可能導致數據不一致。 如果您需要立即讀取寫入數據庫的數據,例如您正在更新配置文件并希望立即呈現它,則可以選擇從master數據庫讀取。 從屬主復制是一個功能非常強大的擴展解決方案,但是它具有相當多的復雜性。 在用盡了更簡單的解決方案并確保在應用程序內進行有效優化之后,實施此解決方案是明智的。
5. 數據庫分片
到目前為止,這些擴展解決方案中的大多數都專注于通過管理對數據庫的讀取來減少負載。 數據庫分片是一種水平擴展解決方案,可通過管理對數據庫的讀寫來管理負載。 這是一種架構模式,涉及將主數據庫拆分(分區)為多個數據庫(分片)的過程,這些數據庫可以更快,更易于管理。
數據庫分片技術有兩種類型:垂直分片和水平分片(請參見下圖)。 使用水平分區時,將表取出并放在不同的機器上,每個表具有相同的列,但行不同。 垂直分區更為復雜,其中涉及在多臺計算機之間拆分一個表。 一個表被分離出來并放入新的不同表中。 一個垂直分區中保存的數據獨立于所有其他分區中的數據,每個表都包含不同的行和列。


兩種分片技術都有助于水平擴展,也稱為"向外擴展",這使您可以在系統中添加更多機器以分配/分散負載。 水平擴展通常與垂直擴展(也稱為"向上擴展")形成對比,后者涉及升級現有服務器的硬件。 擴展數據庫相對簡單,盡管任何非分布式數據庫在計算能力和存儲方面都有其局限性,因此擁有自由擴展的自由度可使您的系統更加靈活。
分片的數據庫體系結構還可以顯著提高應用程序查詢的速度,并提供增強的故障恢復能力。 在未分片的數據庫上提交查詢時,它可能不得不搜索表中的每一行,這可能會非常慢。 或者,通過將一個表拆分為多個表,查詢必須遍歷更少的記錄才能返回結果。 由于每個表都在單獨的服務器上,因此減輕了服務器不可用帶來的影響。 對于分片的數據庫,與未分片的數據庫相比,中斷的影響可能僅影響單個分片,在未分片的數據庫中,中斷可能使整個應用程序不可用。
具有分片的數據庫體系結構可帶來一些巨大的好處,但是,它很復雜且實現和維護成本很高。 在用完其他擴展解決方案之后,絕對可以考慮使用此選項,因為無效實施的后果可能非常嚴重。
結論
恭喜,您的應用程序現在已經有了適當的解決方案,可以成功處理數據庫負載并隨著應用程序的成功進行擴展! 盡管還沒有足夠的時間來慶祝……有效擴展的服務器是高性能和可靠應用程序不可或缺的一部分。