「PostgreSQL架構」Citus實時執行程序如何並行化Postgres查詢

Citus有多個不同的執行程序,每個執行程序的行為都不同,以支持各種用例。對於許多概念而言,分佈式SQL似乎必須是一個複雜的概念,但是其原理並不是火箭科學。在這裡,我們將看幾個有關Citus如何採用標準SQL並將其轉換為以分佈式形式運行以便可以並行化的示例。結果是您可以看到單節點數據庫的查詢性能提高了100倍或更多。

我們如何知道某物是分佈式的還是單片?

在瞭解實時執行器的工作方式之前,值得對Citus執行器進行全面的複習。

當Citus收到查詢時,我們首先查看它是否具有where子句的分片鍵(也稱為分發列)。如果您要分拆諸如CRM應用程序之類的多租戶應用程序,則可能會有一個org_id,您總是會限制查詢。在這種情況下,只要org_id是where子句的一部分,我們就知道它的目標是單個分片,因此可以使用路由器執行程序。如果未使用該查詢,我們會將查詢拆分並跨節點並行發送給所有分片。

作為快速更新,Citus中的一個表是另一個表。如果您有一個表事件並想要分發它,則可以創建32個分片,這意味著我們可以輕鬆擴展到32個節點。如果您從2個節點開始,則每個節點包含32個分片。這意味著每個節點將一次接收16個查詢,並且如果它有16個可用的內核,那麼所有工作將並行完成,從而導致2個節點x 16個內核,或者說,與在單個內核上執行相比,速度提高了32倍。

對於後面的示例,我們將僅創建4個分片以簡化它們,但是隨著添加的分片和對應的內核的增加,事情幾乎線性地擴展。

用SQL編寫,用MapReduce思考

Citus對實時分析的支持是自從我們早期以來,人們就一直使用Citus的工作負載,這要歸功於我們先進的查詢並行化。結果就是您能夠用標準SQL表示事物,並讓Citus的分佈式查詢計劃器完成重寫查詢的艱苦工作,從而為您提供出色的性能,而無需創建複雜的工程膠帶。

深入研究一些示例,從count(*)開始

我們可以開始處理的最簡單的查詢是count(*)。對於count(*),我們需要從每個分片中獲取一個count(*)。首先,針對事件表運行一個解釋計劃,以瞭解其運作方式:

QUERY PLAN ------------------------------------------------------------------------------------------------------- Aggregate (cost=0.00..0.00 rows=0 width=0) -> Custom Scan (Citus Real-Time) (cost=0.00..0.00 rows=0 width=0) Task Count: 4 Tasks Shown: One of 4 -> Task Node: host=ec2-18-233-232-9.compute-1.amazonaws.com port=5432 dbname=citus -> Aggregate (cost=11.62..11.63 rows=1 width=8) -> Seq Scan on events_102329 events (cost=0.00..11.30 rows=130 width=0) (8 rows) Time: 160.596 ms

查詢中有一些注意事項。 首先是它使用的是Citus Real-Time執行程序,這意味著查詢正在擊中所有碎片。 第二個是任務是4個之一。該任務在所有節點上通常是相同的,但是由於它是純粹的Postgres計劃,可以根據數據分佈和估算值進行更改。 如果要查看所有查詢計劃,則可以擴展輸出以獲取所有4個分片的任務。 最後,您具有針對該特定分片的查詢計劃本身。

讓我們以集群示例為例:

「PostgreSQL架構」Citus實時執行程序如何並行化Postgres查詢

如果我們要對該集群執行count(*),Citus將重新編寫查詢並將四個count(*)查詢發送到每個分片。 然後將所得的計數返回給協調器以執行最終聚合:

「PostgreSQL架構」Citus實時執行程序如何並行化Postgres查詢

性能遠遠超過count(*)

雖然count(*)很容易看出它是如何工作的,但是您可以執行更多操作。 如果要獲得四個平均值並將它們平均在一起,則實際上並不會獲得結果平均值。 相反,對於普通的Citus將執行sum(foo)和count(foo),然後在協調器上將sum(foo)/ count(foo)相除,以得出正確的結果。 最好的部分仍然可以編寫AVG,Citus負責底層的複雜性。

除了彙總之外,Citus還可以告訴您何時加入並在本地執行這些加入。 讓我們向事件表中添加另一個表:會話。 現在,對於每個事件,我們都將會話ID記錄為其中的一部分,以便我們加入。 有了這兩個表,我們現在想要一個查詢,該查詢將告訴我們會話的平均事件數,以及上週創建的會話:

SELECT count(events.*),

count(distinct session_id)

FROM events,

sessions

WHERE sessions.created_at >= now() - '1 week'::interval

AND sessions.id = events.session_id

在這兩個表上都分配有會話ID的情況下,Citus會知道這些表在同一位置。 使用共置的表,Citus將重新編寫查詢以將連接向下推送到本地,從而不會通過網絡發送太多數據。 結果是,我們將從每個分片(而不是所有原始數據)中將2條記錄發送回協調器,從而大大縮短了分析查詢時間。 內部重寫的內容可能類似於:

SELECT count(events_01.*),

count(distinct session_id)

FROM events_01,

sessions_01

WHERE sessions_01.created_at >= now() - '1 week'::interval

AND sessions_01.id = events_01.session_id

SELECT count(events_02.*),

count(distinct session_id)

FROM events_02,

sessions_02

WHERE sessions_02.created_at >= now() - '1 week'::interval

AND sessions_02.id = events_02.session_id

...

分佈式SQL不一定很困難,但是可以肯定很快

下推連接和並行化的好處是:

  • 您不必通過網絡發送太多數據,這比在內存中掃描要慢
  • 您可以一次利用系統中的所有內核,而不是在單個內核上運行查詢
  • 您可以超出可以在一臺計算機中裝載多少內存/內核的限制

希望這次對Citus實時執行器的瀏覽簡化了幕後工作的方式。 如果您想更深入地學習,請閱讀我們的文檔。

原文:https://www.citusdata.com/blog/2018/08/17/breaking-down-citus-real-time-executor/

本文:http://jiagoushi.pro/node/927

討論:請加入知識星球或者微信圈子【首席架構師圈】


分享到:


相關文章: