數(shù)據(jù)庫分片技術(shù):深入解析與實踐指南
前言
任何應用程序或網(wǎng)站,如果經(jīng)歷了顯著增長,最終都需要進行擴展以適應流量的增加。對于數(shù)據(jù)驅(qū)動的應用程序和網(wǎng)站來說,擴展的方式必須確保數(shù)據(jù)的安全性和完整性。很難預測一個網(wǎng)站或應用程序會變得多受歡迎,或者這種受歡迎程度能維持多久,這就是為什么一些組織選擇能夠動態(tài)擴展數(shù)據(jù)庫的數(shù)據(jù)庫架構(gòu)。在這篇概念性文章中,我們將討論這樣一種數(shù)據(jù)庫架構(gòu):分片數(shù)據(jù)庫。分片在近年來受到了大量關(guān)注,但許多人對它是什么或者在什么情況下對數(shù)據(jù)庫進行分片是有意義的并沒有清晰的理解。我們將討論分片是什么,它的一些主要優(yōu)點和缺點,以及一些常見的分片方法。
什么是分片
分片是一種數(shù)據(jù)庫架構(gòu)模式,與水平分區(qū)有關(guān)——即將一個表的行分離到多個不同的表中,這些表被稱為分區(qū)。每個分區(qū)都有相同的模式和列,但行完全不同。同樣,每個分區(qū)中保存的數(shù)據(jù)是獨特的,并且與其它分區(qū)中保存的數(shù)據(jù)互不影響。
我們可以從水平分區(qū)與垂直分區(qū)的關(guān)系來幫助理解水平分區(qū)。在垂直分區(qū)的表中,整個列被分離出來并放入新的、不同的表中。一個垂直分區(qū)中保存的數(shù)據(jù)與所有其他分區(qū)中的數(shù)據(jù)是獨立的,并且每個分區(qū)都保存著不同的行和列。下面的圖示展示了一個表如何同時進行水平和垂直分區(qū):
圖片
分片涉及將數(shù)據(jù)分成兩個或更多的小塊,稱為邏輯分片。這些邏輯分片隨后被分布到不同的數(shù)據(jù)庫節(jié)點上,這些節(jié)點被稱為物理分片,可以保存多個邏輯分片。盡管如此,所有分片中保存的數(shù)據(jù)集體代表了整個邏輯數(shù)據(jù)集。
數(shù)據(jù)庫分片體現(xiàn)了一種無共享架構(gòu)。這意味著分片是自治的;它們不共享任何相同的數(shù)據(jù)或計算資源。然而,在某些情況下,將某些表復制到每個分片中作為引用表可能是有意義的。例如,假設有一個應用程序的數(shù)據(jù)庫依賴于固定的重量測量轉(zhuǎn)換率。通過將包含必要轉(zhuǎn)換率數(shù)據(jù)的表復制到每個分片中,它將有助于確保所有分片都保存了查詢所需的所有數(shù)據(jù)。
通常,分片是在應用程序級別實現(xiàn)的,這意味著應用程序包含定義傳輸讀取和寫入到哪個分片的代碼。然而,一些數(shù)據(jù)庫管理系統(tǒng)內(nèi)置了分片功能,允許你直接在數(shù)據(jù)庫級別實現(xiàn)分片。
鑒于我們對分片的一般概述,讓我們來看看與這種數(shù)據(jù)庫架構(gòu)相關(guān)的一些優(yōu)點和缺點。
分片的優(yōu)點
分片數(shù)據(jù)庫的主要吸引力在于它可以幫助實現(xiàn)水平擴展,也稱為擴展外部。水平擴展是在現(xiàn)有堆棧中添加更多機器以分散負載并允許更多流量和更快處理的做法。這通常與垂直擴展相對比,后者稱為擴展上行,涉及升級現(xiàn)有服務器的硬件,通常是通過增加更多RAM或CPU。
在單臺機器上運行關(guān)系數(shù)據(jù)庫并根據(jù)需要通過升級其計算資源來擴展它是相對簡單的。然而,任何非分布式數(shù)據(jù)庫在存儲和計算能力方面都將受到限制,因此具有水平擴展的自由使你的設置更加靈活。
選擇分片數(shù)據(jù)庫架構(gòu)的另一個原因是加快查詢響應時間。當你在未分片的數(shù)據(jù)庫上提交查詢時,它可能必須搜索你查詢的表中的所有行,才能找到你正在尋找的結(jié)果集。對于具有大型、單一數(shù)據(jù)庫的應用程序,查詢可能會變得非常慢。通過將一個表分片成多個,查詢必須覆蓋的行數(shù)減少,結(jié)果集返回得更快。
分片還可以通過減輕停機的影響來使應用程序更可靠。如果你的應用程序或網(wǎng)站依賴于未分片的數(shù)據(jù)庫,停機可能會使整個應用程序不可用。通過分片數(shù)據(jù)庫,停機可能只會影響單個分片。盡管這可能會使應用程序或網(wǎng)站的某些部分對某些用戶不可用,但總體影響仍然會比整個數(shù)據(jù)庫崩潰時要小。
分片的缺點
雖然分片數(shù)據(jù)庫可以使擴展更容易并提高性能,但它也可能帶來一定的限制。在這里,我們將討論其中的一些,并解釋為什么它們可能是完全避免分片的原因。
人們遇到分片的第一個困難是正確實現(xiàn)分片數(shù)據(jù)庫架構(gòu)的復雜性。如果做得不正確,分片過程可能導致數(shù)據(jù)丟失或表損壞的風險很大。即使做得正確,分片也可能對你的團隊工作流程產(chǎn)生重大影響。用戶必須管理跨越多個分片位置的數(shù)據(jù),而不是從一個單一的入口點訪問和管理數(shù)據(jù),這可能會對某些團隊造成干擾。
用戶在分片數(shù)據(jù)庫后有時會遇到的一個問題是分片最終變得不平衡。舉個例子,假設你有一個數(shù)據(jù)庫有兩個獨立的分片,一個用于姓氏以字母A到M開頭的客戶,另一個用于姓氏以字母N到Z開頭的客戶。然而,你的應用程序為姓氏以字母G開頭的人提供了過多服務。因此,A-M分片逐漸積累了比N-Z分片更多的數(shù)據(jù),導致應用程序減速并為大量用戶停滯不前。A-M分片已經(jīng)成為了所謂的數(shù)據(jù)庫熱點。在這種情況下,分片數(shù)據(jù)庫的任何好處都被減速和崩潰所抵消。數(shù)據(jù)庫可能需要修復和重新分片,以實現(xiàn)更均勻的數(shù)據(jù)分布。
另一個主要缺點是一旦數(shù)據(jù)庫被分片,就很難將其恢復到未分片的架構(gòu)。在分片之前制作的數(shù)據(jù)庫備份不會包括自分區(qū)以來寫入的數(shù)據(jù)。因此,重建原始未分片架構(gòu)需要將新分區(qū)數(shù)據(jù)與舊備份合并,或者將分區(qū)數(shù)據(jù)庫轉(zhuǎn)換回單個數(shù)據(jù)庫,這兩者都是昂貴且耗時的努力。
最后一個需要考慮的缺點是分片不是每個數(shù)據(jù)庫引擎都原生支持的。例如,PostgreSQL不包括自動分片作為功能,盡管可以手動分片PostgreSQL數(shù)據(jù)庫。有一些Postgres分支確實包括自動分片,但這些通常落后于最新的PostgreSQL發(fā)布,并且缺少某些其他功能。一些專門的數(shù)據(jù)庫技術(shù)——如MySQL Cluster或某些數(shù)據(jù)庫即服務產(chǎn)品,如MongoDB Atlas——確實包括自動分片作為功能,但這些數(shù)據(jù)庫管理系統(tǒng)的普通版本則不支持。因此,分片通常需要“自己搞定”的方法。這意味著分片的文檔或故障排除提示通常很難找到。
當然,這些只是分片前的一些問題。根據(jù)其用例,分片數(shù)據(jù)庫可能還有許多其他潛在的缺點。
分片架構(gòu)
一旦你決定分片你的數(shù)據(jù)庫,接下來你需要弄清楚你將如何進行。在運行查詢或?qū)魅霐?shù)據(jù)分布到分片表或數(shù)據(jù)庫時,確保它去到正確的分片是至關(guān)重要的。否則,可能會導致數(shù)據(jù)丟失或查詢緩慢。在這一部分,我們將討論一些常見的分片架構(gòu),每種架構(gòu)都使用稍微不同的過程來在分片之間分布數(shù)據(jù)。
基于鍵的分片
基于鍵的分片,也稱為基于哈希的分片,涉及使用從新寫入數(shù)據(jù)中取出的值——例如客戶的ID號、客戶端應用程序的IP地址、郵政編碼等——并將其插入哈希函數(shù)以確定數(shù)據(jù)應該去哪個分片。哈希函數(shù)是一種函數(shù),它將數(shù)據(jù)(例如客戶電子郵件)作為輸入,并輸出一個離散值,稱為哈希值。在分片的情況下,哈希值是一個分片ID,用于確定傳入數(shù)據(jù)將存儲在哪個分片上??偟膩碚f,過程是這樣的:
圖片
為了確保條目以正確的分片和一致的方式放置,輸入哈希函數(shù)的值應該都來自同一列。這個列被稱為分片鍵。簡單來說,分片鍵類似于主鍵,因為兩者都用于為單個行建立唯一標識符。廣義上講,分片鍵應該是靜態(tài)的,這意味著它不應該包含隨時間變化的值。否則,它將增加更新操作的工作量,并可能減慢性能。
雖然基于鍵的分片是一種相當常見的分片架構(gòu),但在嘗試動態(tài)添加或刪除數(shù)據(jù)庫服務器時,它可能會使事情變得棘手。當你添加服務器時,每個服務器都需要一個相應的哈希值,如果你不添加它們,許多現(xiàn)有條目,如果不是全部的話,都需要重新映射到它們新的、正確的哈希值,然后遷移到適當?shù)姆掌鳌.斈汩_始重新平衡數(shù)據(jù)時,新的和舊的哈希函數(shù)都將無效。因此,你的服務器在遷移期間將無法寫入任何新數(shù)據(jù),你的應用程序可能會受到停機的影響。
這種策略的主要吸引力在于它可以用來均勻地分布數(shù)據(jù),以防止熱點。此外,因為它算法性地分布數(shù)據(jù),所以不需要像范圍或目錄基于分片的其他策略那樣維護所有數(shù)據(jù)位置的映射。
基于范圍的分片
基于范圍的分片涉及根據(jù)給定值的范圍進行分片數(shù)據(jù)。為了說明,假設你有一個數(shù)據(jù)庫存儲了零售商目錄中的所有產(chǎn)品信息。你可以創(chuàng)建幾個不同的分片,并根據(jù)它們所屬的價格范圍分配每個產(chǎn)品的信息,如下所示:
圖片
基于范圍的分片的主要好處是它相對容易實現(xiàn)。每個分片保存不同的數(shù)據(jù)集,但它們都有一個與原始數(shù)據(jù)庫相同的模式。
應用程序代碼讀取數(shù)據(jù)屬于哪個范圍,然后將其寫入相應的分片。
另一方面,基于范圍的分片不能防止數(shù)據(jù)分布不均,導致上述數(shù)據(jù)庫熱點??纯词纠龍D,即使每個分片保存相等數(shù)量的數(shù)據(jù),特定產(chǎn)品可能會比其他產(chǎn)品受到更多關(guān)注。它們各自的分片將反過來接收不成比例的讀取數(shù)量。
基于目錄的分片
要實現(xiàn)基于目錄的分片,必須創(chuàng)建和維護一個使用分片鍵跟蹤哪個分片保存哪個數(shù)據(jù)的查找表。查找表是一個保存有關(guān)特定數(shù)據(jù)位置的靜態(tài)信息的表。下面的圖示展示了基于目錄分片的簡單示例:
圖片
在這里,配送區(qū)域列被定義為分片鍵。來自分片鍵的數(shù)據(jù)被寫入查找表,以及每行相應的分片。這與基于范圍的分片類似,但不是確定分片鍵數(shù)據(jù)屬于哪個范圍,而是每個鍵都與其自己的特定分片相關(guān)聯(lián)?;谀夸浀姆制窃诜制I基數(shù)較低——意味著它有少量可能的值——并且對于一個分片來說存儲一系列鍵沒有意義的情況下,比基于范圍的分片更好的選擇。請注意,它也與基于鍵的分片不同,因為它不需要通過哈希函數(shù)處理分片鍵;它只是檢查鍵與查找表,看看數(shù)據(jù)需要寫入哪里。
基于目錄的分片的主要吸引力在于其靈活性。基于范圍的分片架構(gòu)限制你指定值的范圍,而基于鍵的分片限制你使用固定的哈希函數(shù),這如前所述,以后更改起來可能非常困難。另一方面,基于目錄的分片允許你使用任何系統(tǒng)或算法將數(shù)據(jù)條目分配給分片,并且使用這種方法動態(tài)添加分片相對容易。
雖然基于目錄的分片是這里討論的分片方法中最靈活的,但每次查詢或?qū)懭肭岸夹枰B接到查找表可能會對應用程序的性能產(chǎn)生不利影響。此外,查找表可能成為單點故障:如果它變得損壞或以其他方式失敗,可能會影響寫入新數(shù)據(jù)或訪問現(xiàn)有數(shù)據(jù)的能力。
我應該分片嗎?
是否應該實施分片數(shù)據(jù)庫架構(gòu)幾乎總是一個有爭議的問題。一些人認為,對于達到一定規(guī)模的數(shù)據(jù)庫來說,分片是不可避免的結(jié)果,而另一些人則認為,由于分片帶來的操作復雜性,除非絕對必要,否則應避免分片。
由于這種增加的復雜性,分片通常只在處理非常大的數(shù)據(jù)量時才執(zhí)行。以下是一些可能有益于分片數(shù)據(jù)庫的常見場景:
- 應用程序數(shù)據(jù)量增長到超過單個數(shù)據(jù)庫節(jié)點的存儲容量。
- 數(shù)據(jù)庫的寫入或讀取量超過單個節(jié)點或其只讀副本的處理能力,導致響應時間變慢或超時。
- 應用程序所需的網(wǎng)絡帶寬超過了單個數(shù)據(jù)庫節(jié)點及其任何只讀副本可用的帶寬,導致響應時間變慢或超時。
在進行分片之前,你應該嘗試所有其他優(yōu)化數(shù)據(jù)庫的選項。你可能需要考慮的一些優(yōu)化包括:
- 設置遠程數(shù)據(jù)庫。如果你正在使用一個所有組件都駐留在同一臺服務器上的單體應用程序,你可以通過將數(shù)據(jù)庫移動到它自己的機器上來提高數(shù)據(jù)庫的性能。這不會像分片那樣增加太多復雜性,因為數(shù)據(jù)庫的表保持完整。然而,它仍然允許你將數(shù)據(jù)庫與其余基礎設施分開垂直擴展。
- 實施緩存。如果你的應用程序的讀取性能是造成問題的原因,緩存是一種可以幫助提高性能的策略。緩存涉及將已經(jīng)請求的數(shù)據(jù)暫時存儲在內(nèi)存中,允許你稍后更快地訪問它。
- 創(chuàng)建一個或多個只讀副本。這是另一種可以幫助提高讀取性能的策略,它涉及將數(shù)據(jù)從一個數(shù)據(jù)庫服務器(主服務器)復制到一個或多個輔助服務器。在此之后,每個新的寫入都會先發(fā)送到主服務器,然后再復制到輔助服務器,而讀取則專門在輔助服務器上進行。像這樣分配讀取和寫入可以防止任何一臺機器承擔過多的負載,有助于防止減速和崩潰。請注意,創(chuàng)建只讀副本涉及更多的計算資源,因此成本更高,這可能對一些人來說是一個重要的約束。
- 升級到更大的服務器。在大多數(shù)情況下,將數(shù)據(jù)庫服務器升級到具有更多資源的機器比分片需要的努力要少。與創(chuàng)建只讀副本一樣,升級的服務器將擁有更多的資源,可能會花費更多的錢。因此,只有在調(diào)整大小確實是你的最佳選擇時,你才應該進行調(diào)整。
請記住,如果你的應用程序或網(wǎng)站增長到一定程度,這些策略中的任何一個都將不足以單獨提高性能。在這種情況下,分片確實可能是你的最佳選擇。
結(jié)論
分片對于那些尋求水平擴展數(shù)據(jù)庫的人來說可能是一個很好的解決方案。然而,它也增加了相當大的復雜性,并為你的應用程序創(chuàng)造了更多的潛在故障點。分片對某些人來說可能是必要的,但創(chuàng)建和維護分片架構(gòu)所需的時間和資源可能會超過其他人的利益。
通過閱讀這篇概念性文章,你應該對分片的利弊有了更清晰的了解。展望未來,你可以利用這些見解來做出更明智的決策,關(guān)于是否分片數(shù)據(jù)庫架構(gòu)適合你的應用程序。