![]() | 博主歷時三年精心創作的《大數據平臺架構與原型實現:數據中臺建設實戰》一書現已由知名IT圖書品牌電子工業出版社博文視點出版發行,點擊《重磅推薦:建大數據平臺太難了!給我發個工程原型吧!》了解圖書詳情,京東購書鏈接:https://item.jd.com/12677623.html,掃描左側二維碼進入京東手機購書頁面。 |
我們可能在一些介紹數據庫 Join 檔中看到 Build 和 Probe,分別代表著 Join 操作中的 右表 和 左表,為什么會有這樣的稱呼呢?原來它們都出自于一種叫 ”Hash Join“ 的 join 算法(常見的 Join 算法有:Hash Join、Loop Join、Merge Join)。先看一下名詞解釋:
-
Hash Join:一種實現 Join 的算法,它通過在 Join 的一側構建 Hash Table 并在另一側不斷匹配 Hash Table 來得到 Join 的結果。
-
Build Side (構建端 / 右表):Hash Join 中用于構建 Hash Table 的一側,稱為 Build Side。多數引擎默認以 Join 的右表作為 Build Side。
-
Probe Side(探查端 / 左表):Hash Join 中用于不斷匹配 Hash Table 的一側,稱為 Probe Side。多數引擎默認以 Join 的左表作為 Probe Side。
下面,簡答介紹一下 Hash Join 的原理,我們基于 Hash join in MySQL 8 一文給出的解釋展開,講解使用的 SQL 示例為:
SELECTgiven_name, country_name
FROMpersons JOIN countries ON persons.country_id = countries.country_id;
Hash Join 的實現分為:構建和探查兩個階段,以下是詳細介紹。
Hash Join 原理:構建階段
在 Hash Join 算法下,當兩張表要 Join 時,SQL 引擎會在內存中創建一張哈希表,然后選擇將其中一張較小的表(按字節度量而不是行數)的數據加載到這張哈希表中,并以 Join 列的值作哈希的 Key。既然是要將表的數據加載到內存中,所以,不難理解算法為什么要選擇加載小表而不是大表。
在上面的 SQL 示例中,countries
表肯定是一張小表,所以它會被加載到內存的哈希表中,也就是成為 Build Side,而 Join 列 country_id
的值經 hash 后的值會作為哈希表中 Key。
? 至于為什么現在都將右表稱為 Build Side,左表稱為 Probe Side,我并沒有找到比較主流的有說服力的觀點,可能是因為算法在最初提出時就是這樣約定的:選擇右表作 Build Side, 左表作 Probe Side,后來隨著 SQL 引擎的優化,雖然能自動選擇小表作為 Build Side 了,但這種稱謂習慣被保留了下來。歡迎了解其中原委的讀者補充
下圖形象地展示了構建階段的工作原理:
Hash Join 原理:探查階段
構建階段完成后,SQL 引擎就從 探測端 逐行讀取記錄,然后用 Join 列的 Hash 值去內存中的哈希表中查找是否有對應記錄,有就是匹配到了 構建端 的記錄,然后聯合兩端的數據作為結果輸出。
同樣以上面的示例 SQL 為例,SQL 引擎逐行讀取 persons
表中的記錄,取出它的 country_id
列進行 hash 處理,以得到的哈希值為 Key 去哈希表中查找,找同相同哈希值的記錄就意味著和 countries
表中的一條記錄 Join 上了。
下圖展示了探查階段的工作原理:
不過,上圖并不算好,沒有把“探查”動作描述出來,下圖相對更加形象一些:
Hash Join 的限制
最后,提醒一下 Hash Join 的限制,其實從上面的原理介紹中你大概能推測出來:由于 Hash Join 是使用 join 列的哈希值進行匹配的,所以,關聯條件中必須包含至少一個 equi join(=)
!
參考資料:
https://www.zhihu.com/question/35906621
https://dev.mysql.com/blog-archive/hash-join-in-mysql-8/