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

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

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


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