背景
最近在閱讀查詢優化器的論文,發現System R中對於Join操作的定義一般分爲了兩種,即嵌套循環、排序-合併聯接。在原文中,更傾向使用排序-合併聯接邏輯。
考慮到我的領域是在處理分庫分表或者其他的分區模式,這讓我開始不由得聯想我們怎麼在分佈式場景應用這個Join邏輯,對於兩個不同庫裏面的不同表我們是沒有辦法直接進行Join操作的。查閱資料後發現原來早有定義,即分佈式聯接算法。
分佈式聯接算法
跨界點處理數據即分佈式聯接算法,常見的有四種模型:Shuffle Join(洗牌聯接)、Broadcast Join(廣播聯接)、MapReduce Join(MapReduce聯接)、Sort-Merge Join(排序-合併聯接)。
接下來將進行逐一瞭解與分析,以便後續開發的應用。
Shuffle Join(洗牌聯接)
先上原理解釋:
Shuffle Join的核心思想是將來自不同節點的數據重新分發(洗牌),使得可以聯接的數據行最終位於同一個節點上。 通常,對於要聯接的兩個表,會對聯接鍵應用相同的哈希函數,哈希函數的結果決定了數據行應該被髮送到哪個節點。這樣,所有具有相同哈希值的行都會被送到同一個節點,然後在該節點上執行聯接操作。
可能解釋完還是有點模糊,舉個例子,有兩張表,分別以id字段進行分庫操作,且哈希算法相同(爲了簡單,這裏只介紹分庫場景,分庫分表同理。算法有很多種,這裏舉例是hash算法),那麼這兩張表的分片或許可以在同一個物理庫中,這樣我們不需要做大表維度的處理,我們可以直接下推Join操作到對應的物理庫操作即可。
在ShardingSphere中,這種場景類似於綁定表的定義,如果兩張表的算法相同,可以直接配置綁定表的關係,進行相同算法的連接查詢,避免複雜的笛卡爾積。
這樣做的好處是可以儘量下推到數據庫操作,在中間件層面我們可以做並行處理,適合大規模的數據操作。
但是,這很理想,有多少表會採用相同算法處理呢。
Broadcast Join(廣播聯接)
先上原理解釋:
當一個表的大小相對較小時,可以將這個小表的全部數據廣播到所有包含另一個表數據的節點上。 每個節點上都有小表的完整副本,因此可以獨立地與本地的大表數據進行聯接操作,而不需要跨節點通信。
舉個例子,有一張非常小的表A,還有一張按照ID分片的表B,我們可以在每一個物理庫中複製一份表A,這樣我們的Join操作就可以直接下推到每一個數據庫操作了。
這種情況比Shuffle Join甚至還有性能高效,這種類似於ShardingSphere中的廣播表的定義,其存在類似於字典表,在每一個數據庫都同時存在一份,每次寫入會同步到多個節點。
這種操作的好處顯而易見,不僅支持並行操作而且性能極佳。
但是缺點也顯而易見,如果小表不夠小數據冗餘不說,廣播可能會消耗大量的網絡帶寬和資源。
MapReduce Join(MapReduce聯接)
先上原理解釋:
MapReduce是一種編程模型,用於處理和生成大數據集,其中的聯接操作可以分爲兩個階段:Map階段和Reduce階段。 Map階段:每個節點讀取其數據分片,並對需要聯接的鍵值對應用一個映射函數,生成中間鍵值對。 Reduce階段: 中間鍵值對會根據鍵進行排序(在某些實現中排序發生在Shuffle階段)和分組,然後發送到Reduce節點。 在Reduce節點上,具有相同鍵的所有值都會聚集在一起,這時就可以執行聯接操作。
MapReduce Join不直接應用於傳統數據庫邏輯,而是適用於Hadoop這樣的分佈式處理系統中。但是爲了方便理解,還是用SQL語言來分析,例如一條SQL:
SELECT orders.order_id, orders.date, customers.name
FROM orders
JOIN customers ON orders.customer_id = customers.customer_id;
會被轉換爲兩個SQL:
SELECT customer_id, order_id, date FROM orders;
SELECT customer_id, name FROM customers;
這個過程就是Map階段,即讀取orders
和customers
表的數據,併爲每條記錄輸出鍵值對,鍵是customer_id
,值是記錄的其餘部分。
下一個階段可有可無,即Shuffle階段。如果不在這裏排序可能會在Map階段執行SQL時候排序/分組或者在接下來的Reduce階段進行額外排序/分組。在這個階段主要將收集到的數據按照customer_id排序分組,以確保相同的customer_id的數據達到Reduce階段。
Reduce階段將每個對應的customer_id進行聯接操作,輸出並返回最後的結果。
這種操作普遍應用於兩個算法完全不相同的表單,也是一種標準的處理模型,在這個過程中,我們以一張邏輯表的維度進行操作。這種算法可能會消耗大量內存,甚至導致內存溢出,並且在處理大數據量時會相當耗時,因此不適合需要低延遲的場景。
額外補充
內存溢出場景普遍在如下場景:
我能想到的相應解決方案:
Sort-Merge Join(排序-合併聯接)
先上原理解釋:
在分佈式環境中,Sort-Merge Join首先在每個節點上對數據進行局部排序,然後將排序後的數據合併起來,最後在合併的數據上執行聯接操作。 這通常涉及到多階段處理,包括局部排序、數據洗牌(重新分發),以及最終的排序和合並。
舉個理解,還是上面的SQL。
SELECT orders.order_id, orders.date, customers.name
FROM orders
JOIN customers ON orders.customer_id = customers.customer_id;
orders
表按customer_id
進行排序。
customers
表按customer_id
進行排序。
customer_id
的行配對。
這個就有點類似於原生的排序-合併聯接了。也是數據庫場景的標準處理辦法。
對於已經排序的數據集或數據分佈均勻的情況,這種方法非常有效。如果數據未預先排序,這種方法可能會非常慢,因爲它要求數據在聯接之前進行排序。
當然,這個算法也會造成內存溢出的場景,解決方案如下:
作者:京東科技 張俊傑
來源:京東雲開發者社區 轉載請註明來源