

Notion 的資料庫擴充套件挑戰
Notion 是一款流行的生產力和協作平臺,可用作一體化工作區。它功能強大,你可以用它來做筆記、建立電子表格、管理日曆、跟蹤時間線等等。
Notion 工程師必須應對的一個關鍵挑戰是在 2020 年中期,這與他們的單體 PostgreSQL 資料庫有關。該公司依靠它已經執行 5 年了,支援其實現了幾個數量級的大規模增長。
然而,隨著 Notion 的使用者群不斷快速擴大,資料庫在應對不斷增加的負載方面遇到了困難。多個指標表明資料庫的壓力越來越大,例如頻繁的 CPU 峰值和大量的待命請求。
主要問題是 PostgreSQL 的 VACUUM 程序(用於清理和釋放磁碟空間)開始定期停止工作。此時,資料庫無法很好地處理其儲存,從而導致效能下降。
Notion 的工程團隊意識到他們當前的資料庫架構已不再可持續。他們需要一個解決方案來解決眼前的問題併為未來的發展奠定基礎。
經過深思熟慮,團隊決定實施資料庫分片。正如我們所討論的,分片是一種水平擴充套件技術,允許資料分佈在多個數據庫例項之間。
分片策略和設計決策
當 Notion 的團隊選擇分片時,他們必須對如何進行分片做出重要的選擇。
第一個大問題是,他們應該分片哪些資料?Notion 的資料模型圍繞著塊的概念。Notion 頁面上的每個專案(一個段落、一張圖片或一個數據庫)都表示為一個塊。這些塊可以包含其他塊,從而形成一個層次結構。
鑑於這種設定,Block 表是分片的最佳候選項。
但這裡有一個棘手的部分:Block 表與其他表有連線。如果他們只對 Block 表進行分片,他們將有許多複雜的跨分片查詢,這可能會減慢速度。
因此,他們做出了一個明智的決定,決定不僅對 Block 表進行分片,而且對與其相關的所有表進行分片。這樣,他們就可以保留連結在一起的資料,而不必執行那些煩人的跨分片查詢。

下一個問題是:如何劃分資料?
Notion 使用者通常在一個工作區內工作,每個塊都屬於一個特定的工作區。因此,他們選擇使用工作區 ID 作為分片鍵。這意味著單個工作區的所有塊和相關資料最終都會位於同一個分片上,從而最大限度地減少了從多個分片獲取資料的需要。
最後,他們必須決定分片的數量。我喜歡這部分,因為它涉及一些數學知識。Notion 決定在 32 個物理資料庫中分佈 480 個邏輯分片。每個物理資料庫將託管 15 個邏輯分片。
為什麼是 480?原因是它可以被許多數字整除,從而可以靈活地新增或刪除物理主機,同時保持分片均勻分佈。例如,它們可以從 32 臺主機擴充套件到 40 臺,再擴充套件到 48 臺主機,每次都實現增量跳躍。
實施和遷移過程
現在 Notion 已經有了分片策略,是時候移動所有資料而不會給使用者帶來麻煩了。
他們想出了一個聰明的方法來讓這一切順利進行。
首先是雙寫階段。他們設定好後,在移動現有資料的同時,新資料會寫入新舊資料庫。
但有趣的是,他們沒有直接向兩個資料庫寫入資料(如果出現問題,這可能會導致不一致),而是使用了審計日誌。
基本上,他們會記錄舊資料庫中發生的所有寫入操作,然後透過單獨的流程將這些更改應用到新的共享資料庫中。這樣,即使過程中出現問題,他們也總能跟上進度。
為了將所有舊資料移至新的分片設定,Notion 團隊編寫了一個可以複製所有現有資料的指令碼,並在一臺擁有 96 個 CPU 的強大機器上執行該指令碼。即使擁有如此強大的功能,仍然需要大約三天的時間才能將所有資料移至新分片設定。這讓您大致瞭解我們談論的資料量有多大!
但僅僅移動資料還不夠——他們必須確保資料正確。因此,他們想出了兩種方法來驗證資料:
-
他們編寫了一個驗證指令碼來比較新舊資料庫之間的記錄。他們隨機抽取 UUID 並進行檢查,以節省時間,因為檢查每條記錄將花費很長時間。
-
他們還做了一些稱為“暗讀”的事情。對於某些請求,他們會從新舊資料庫中獲取資料,進行比較,並記錄任何差異。他們不會將新資料庫中的資料用於這些請求(因此稱為“暗讀”),但它讓他們可以檢查所有內容是否匹配,而不會影響使用者。
所有這些工作都得到了回報。當需要切換到新的分片設定時,他們只需幾分鐘的停機時間即可完成。對於 Notion 的使用者來說,這主要看起來像是短暫的不可用,但幕後發生了巨大的變化。
經驗教訓
儘管分片過程有效,但 Notion 團隊也學到了一些重要的教訓。
他們意識到的第一件事是他們應該早點開始。等到現有資料庫陷入困境時才開始遷移,會讓遷移過程更具挑戰性。他們必須非常小心地進行遷移,以免給系統帶來更多壓力。
另一個重要收穫是,零停機時間非常重要。雖然團隊設法在幾分鐘的停機時間內完成了切換,但他們意識到,如果他們在追趕指令碼上多下功夫,他們本可以在不中斷的情況下完成切換。
他們還了解到選擇正確的分片鍵有多麼重要。使用工作區 ID 是一個很棒的決定,因為它將相關資料放在一起,並最大限度地減少了對複雜的跨分片查詢的需求。但是,他們注意到,如果將主鍵和分割槽鍵放在一個列中,應用程式程式碼可能會更容易編寫。
儘管吸取了所有教訓,但對於 Notion 使用者來說,分片過程的結果是產品速度明顯加快。
新設定能夠更好地處理負載,從而使執行更加順暢並加快了響應時間。
作者:費爾南多參考:https://newsletter.francofernando.com/p/database-sharding-case-study-postgresql
相關閱讀: