實作查詢並行性是我們縮小 Postgres 和YugabyteDB之間效能差距的關鍵部分。
自成立以來,我們一直透過將執行下推到儲存層來逐步提高 YugabyteDB 的查詢並行性水準。 YugabyteDB 目前支援各種掃描過濾器,以及由多個節點並行執行的簡單聚合。
那為什麼我們需要 Postgres 並行查詢功能呢?
總的來說,這是我們更大努力的一部分,旨在使 YugabyteDB 不僅像 Postgres 一樣工作,而且像它一樣執行(或更好!)
並行查詢允許 PostgreSQL 在同一台機器上運行的 挪威 電話號碼庫 多個進程之間分配執行。它們還支援更大、更複雜的計劃的並行化,包括連接、排序和分組。將 Postgres 平行查詢整合到 YugabyteDB 中,將平行性提升到一個新的水平,而不僅僅是掃描。
在本部落格中,我們深入探討了最新版本YugabyteDB中支援和可用的 Postgres 平行查詢功能。我們將分享查詢並行性如何在 Postgres 中實現和工作,以及我們如何確保該功能在 YugabyteDB 中運作。劇透警告:Postgres 全球開發小組完成了這項繁重的工作,透過對 YugabyteDB 儲存架構採用平行查詢,我們已經實現了 2 倍的效能提升!
為什麼查詢並行性很重要?
並行是一種透過使用更多資源(例如 CPU、記憶體、I/O 等)來更快地完成作業的方法。
並行化作業的一種方法是將其分成更小的部分,並分配多個工作人員來處理這些部分,如下所示。
順序執行與並行執行
圖 1 順序執行與平行執行
多個工作人員並行執行多個任務所需的時間顯著減少。
每個工人都有自己的一組資源,一般來說,所有工人在單位時間內完成的資源加起來大於一個工人單獨分配的資源。當完成各部分所需的資源等於或至少接近完成整個任務所需的資源時,這一點就成立。
然而,並行性需要一些努力和開銷。
工作分解需要資源,也需要結果的組合。因此,並行化一個微小的任務是沒有意義的,因為開銷可能與完成整個任務所需的工作量相當。開銷取決於任務的性質,有些工作比其他工作更容易拆分。
另一個開銷是工作人員之間的通訊。理想情況下,工人應該獨立完成分配的部分。但在實踐中,溝通往往是必要的。例如,當少數工人處理大量零件(工人池)時,準備承擔另一零件的每個工人都需要確保沒有其他人正在處理該零件或試圖這樣做。這種通訊可能會產生大量開銷。工作執行緒可能需要具有獨佔鎖才能存取共用任務清單。工人多、任務小以及分配任務需要很長時間都會增加工人等待鎖的機率,並增加完成專案的時間、精力和成本。
分散式資料庫中的平行性
如果我們考慮資料庫世界中的平行性,分散式資料庫有一種自然地分割其查詢作業的方法。
分散式資料庫通常會對資料進行分片。這意味著,他們將資料集分解 making science 榮獲伊比利亞 google cloud 頒發的 2024 年度服務合作夥伴獎 為區塊並將每個區塊單獨儲存在多個網路節點上。以分片分割查詢是一個簡 單的決定。分割工作量很小,它們只是列出受查詢影響的分片列表,並為每個分片執行一個工作執行緒。
分散式資料庫中的順序掃描
圖2 分散式資料庫中的順序掃描
以上是分散式表的順序掃描的一種可能實作。分片託管在不同的節點上,可以並行工作以形成對其資料請求的回應。在此實作中,查詢引擎執行過濾和投影。
如果儲存節點是「智慧」的並且可以計算表達式,則過濾條件和已使用列的清單可能會包含在請求中,因此資料會在原處進行過濾,從而減少網路流量。下面的圖 3 顯示了這種可能的實作方式。
注意:在遠端節點上執行投影並不總是有意義。投影行可能比過濾後的列值寬,因此可能會增加網路流量。
具有智慧型儲存的分散式資料庫中的順序掃描
圖3 智慧型儲存分散式資料庫中的順序掃描
如果大部分工作被分配給 N 個工人,則完成所有工作大約需要 1/N 的時間。實際上,它不僅如此,因為查詢處理器必須完成一部分工作:準備請求、處理回應、進行預測等。
數據可能會不均勻地分片,並且有些工作人員 台灣數據 比其他工作人員完成得晚。當所有工作人員都完成時,查詢才被視為完成,因此實際時間高於 1/N th,只有在理想情況下才接近這個水平。
這些考慮因素對於掃描來說是正確的,
但一般來說,並行化的效果取決於並行化任務的複雜性。
例如,最佳排序演算法的複雜度是 O(Nlog(N))。如果表格被分成 N 個分片,則同時對所有分片進行排序需要 1/Nlog(N) 的時間。在這種特殊情況下,開銷會更高,因為查詢層必須對工作結果執行合併排序,這是 O(log(N)),但是,一般來說,任務越複雜,結果就越好並行化。
YugabyteDB 從一開始就對其資料進行分片。隨著時間的推移,我們透過並行掃描分片、過濾掉未使用的列以及在執行表和索引掃描時在儲存層應用過濾器來顯著提高並行度。我們的儲存節點甚至可以做簡單的聚合。
然而,在我們整合 Postgres 並行查詢功能之前,我們一直依賴單一表格掃描。現在我們將深入探討如何實現這一目標的細節。
Postgres 查詢平行性
從歷史上看,Postgres 是一個整體 DBMS。它運行在單一電腦上,不會對資料進行分片,其查詢執行引擎是單進程/單執行緒。
這種架構最初運作良好,但當多核心 CPU 成為常態時,開發人員開始尋找一種更好地利用運算能力的方法。
其中一種方法是並行執行查詢。
雖然沒有正式分片,但 Postgres 表資料儲存在頁中。頁面是並行工作的良好單位。頁面大小統一,資料行僅駐留在一頁中(沒有間隙或重疊),並且由於枚舉了頁面,因此很容易同步其處理,因為只需要原子計數器。若要取得另一個單元,工作人員會自動遞增計數器,從而傳回要處理的下一頁。如果計數器數字高於總頁數,則表示工作已完成。
平行順序掃描
圖4 Postgres並行順序掃描
雖然 Postgres 的後端是單進程/單線程,但 Postgres 是一個多進程應用程序,其中每個客戶端連接都由單獨的進程提供服務。
所有進程都可以存取共享記憶體中的表頁緩存,並行工作線程擁有一個共享記憶體來同步其工作。在並行順序掃描的情況下,它是原子頁計數器。圖 4 顯示了 Postgres 平行掃描細節。為了簡單起見,有兩個並行工作人員,但也可能有多個。所示的收集節點用於整合來自工作人員的資料。 Gather 的另一個(未圖示)職責是設定進程來執行平行工作執行緒。
Postgres 進程是有狀態的。事務隔離是狀態最重要的部分。對於查詢應該或不應該看到並發事務所所做的更改有一些規則。使用者可能被授權或未被授權存取特定資料。有一些配置參數可能會影響查詢。構成狀態的所有這些變數都必須從主程序複製到並行工作進程。正確複製狀態以確保讀取一致至關重要。換句話說,無論哪個進程掃描特定頁面,結果都是相同的。正確複製多個變數值所代表的狀態是 Postgres 提交者和維護者必須解決的一個大問題。
一旦問題解決,Postgres 就可以並行執行掃描,並執行過濾和投影等相關任務。但它可以更多地並行化嗎?答案是肯定的。
要了解如何操作,讓我們看看常規的 Postgres 查詢計劃。
並排顯示順序掃描和連接
圖 5 顯示了順序掃描和並排連接。
讓我們專注於代表數據的箭頭。掃描有一個傳入箭頭,連接有兩個。兩者都有一個向外的箭頭。這些資料流被組織成列和行,因此掃描和連接可以以某種方式處理行和列。我們將這些有組織的資料流稱為「關係」。
任何 Postgres 計劃都會發出一個關係。現在看看標示為 t、t1、t2 的矩形。它們看起來像桌子。但是表被組織成列和行,因此表是關係。我們可以在這裡替換任何東西,發出一個關係、一個視圖、一個集合返回函數或另一個 Postgres 計劃。透過將關係定義為行和列的抽象集合,僅使用兩種類型的計劃節點,我們就可以建構連接多個表的任意複雜查詢計劃。
Postgres 有許多其他類型的計劃節點:排序、聚合,甚至實現不同演算法的各種掃描和連接。圖 4 中的 Gather 節點也是一種計劃節點,因此它可以在 Postgres 查詢計劃中使用。
收集下方的數據箭頭也是關係。但是,他們很特別。當聚集時,並行計劃節點發出的關係相當於類似的非分散式計劃發出的關係。從某種意義上說,並行計劃對關係的數據進行了分片。雖然所有分片都位於同一節點上,但它們就像是分散式資料庫的分片。
許多 Postgres 計劃節點可以在平行計劃中正確使用。這意味著,如果計劃節點的多個實例採用正確的「分片」關係作為輸入,那麼它們的輸出就是正確的「分片」關係。如果輸入是非分片關係,這相當於由同一節點發出的關係。某些 Postgres 計劃節點(例如 Merge Join)無法在並行計劃中正常運作。有些可以有條件地工作。
圖 6 顯示了並行連接。該演算法有一個內連接的嵌套循環實作。很容易看出,如果 t1 被分片,而 t2 沒有分片,則演算法是正確的,因為所有連接實例都與 t1 中的每一行相符。
如果嵌套循環實現外連接,那麼當 t1 位於外側時,演算法仍然是正確的,因為我們可以判斷 t1 中的行是否沒有匹配項。但這是不正確的,如果 t2 位於外側,就好像我們在本地節點上沒有匹配 t2 行一樣,我們無法判斷 t2 是否在其他任何地方匹配。因此,嵌套循環演算法在條件上是正確的。
為什麼 Postgres 查詢並行性對 YugabyteDB 有用?
Postgres 平行性是否比 YugabyteDB 等分散式資料庫中實現的平行性更好?坦白說,不。
Postgres 仍然是單節點 DBMS,可用資源是安裝在節點上的資源——相同數量的記憶體和相同數量的 CPU 核心。分散式資料庫可以在幾乎無限數量的節點之間傳播數據,提供幾乎無限的並行性。雲端資料庫(例如 YugabyteDB)可以輕鬆擴展,調整並行層級以滿足需求。
然而,Postgres 並行性在幾個方面與 YugabyteDB 並行性是互補的:
YugabyteDB 支援特殊類型的表,稱為共置表。這些表格不是分散式的,它們及其索引共用一台平板電腦。共置最適合小型表,但也有共置表很大的狀況。 Postgres 並行查詢允許並行處理並置表格。
當我們提到與 YugabyteDB 相關的分散式資料並行處理時,我們通常指的是掃描。我們可以並行執行順序或索引掃描、套用篩選器並評估匹配行上的投影。 YugabyteDB 支援簡單聚合。然而,Postgres 可以並行化複雜的計劃,包括連接、排序、雜湊和聚合,我們也希望在 YugabyteDB 中支援這一點。
我們如何讓它發揮作用
在 YugabyteDB 中啟用 Postgres 並行查詢並不需要太多努力。 YugabyteDB 的查詢計劃是 Postgres 查詢計劃。掃描結果有差異,因為 YugabyteDB 用自己的分散式儲存引擎取代了 Postgres 儲存引擎。然而,我們必須想出自己的方法來在工人之間分配任務。
YugabyteDB 儲存是基於 LSM 的,與 Postgres 枚舉頁面不同。我們決定使用 LSM 索引來建立一個鍵序列,其中相鄰鍵之間的資料量大致相等。
大表可能有太多給定大小的區塊,因此返回太多鍵。所以,我們實作了一個儲存層的功能,可以回傳頁面中的key。
它將起始鍵作為參數,並獲取鍵,直到達到鍵限制、資料大小限製或表末尾。鍵被放置在共享記憶體緩衝區中,工作人員可以存取它們。
當工作人員需要下一個工作區塊時,它會從緩衝區中刪除第一個鍵並複製第二個鍵。這些鍵分別成為掃描的下限和上限。如果工作人員發現緩衝區中的鍵數量不足,則會停止掃描工作以從儲存層取得下一頁鍵。緩衝區中的最後一個鍵成為起始鍵,工作人員可以根據緩衝區中的可用空間估計下一頁大小的限制和所需鍵的數量。當回應到達時,工作人員將金鑰放入緩衝區並繼續掃描工作。
工作人員需要鎖定共享記憶體緩衝區以獲取鍵範圍或添加更多鍵。需要更大的緩衝區來發出更少的金鑰請求。加上需要從儲存層取得金鑰,Yugabyte 中的 Postgres 並行查詢需要比 Postgres 原子計數器更多的開銷。
YugabyteDB 中的平行順序掃描
圖 7 YugabyteDB 中的平行順序掃描
圖7展示了YugabyteDB的平行掃描。與圖4中的Postgres實作相比,有一個密鑰緩衝區(密鑰緩衝區的填充過程未顯示)取代了計數器,並且儲存是遠端和分散式的。
除了圖 3 所示的分散式順序掃描之外,還有一個請求調度程序,可以根據請求的範圍來尋找目標分片。請求調度程序強調了這樣一個事實:工作人員可以從任何分片獲取範圍。它在我們整合並行查詢之前就已經存在,但它的使用受到限制。基本上,如果查詢對鍵列有條件,請求調度程式可以過濾掉一些分片。
不難看出,YugabyteDB對分散式表的掃描符合Postgres平行計畫的定義,因此可以與Postgres並行查詢整合。
另一個挑戰是將事務狀態從主後端傳輸到後台工作人員。 Postgres 複製交易 ID 和快照,但在 YugabyteDB 中,交易是分散式的,它們的狀態以及 MVCC 可見性資料與 Postgres 不同。我們必須正確地將資料複製到後台工作人員,以確保所有工作人員在交易上保持一致。
下一步
並行 Postgres 功能現在可用於並置表。請觀看我們最近的YFTT 劇集,以了解更多見解和演示。
在使用兩位後台工作人員的測試中,我們已經在某些查詢中實現了超過 2 倍的效能提升。下一步是支援哈希和範圍分佈式表。我們還需要更好地降低並行掃描的成本,以便優化器在有多個選項可用時做出更好的選擇。
從長遠來看,我們希望能夠將並行子計畫實例傳送到其他節點。這將使我們能夠使用更多的工作人員並增加並行性。請繼續關注我們的下一次更新!
若要詳細了解YugabyteDB 2024.1中引入的強大新功能和架構增強功能,以實現增強的 Postgres 相容性,請查看我們的最新版本頁面。