這是一個很有意思卻很少有人注意的問題。
當我用Google搜尋MySQL
這個關鍵詞的時候,Google只提供了13
頁的搜尋結果,我通過修改url的分頁引數試圖搜尋第14
頁資料,結果出現了以下的錯誤提示:
百度搜尋同樣不提供無限分頁,對於MySQL
關鍵詞,百度搜尋提供了76
頁的搜尋結果。
強如Google搜尋,為什麼不支援無限分頁?無非有兩種可能:
「做不到」是不可能的,唯一的理由就是「沒必要」。
首先,當第1頁的搜尋結果沒有我們需要的內容的時候,我們通常會立即更換關鍵詞,而不是翻第2頁,更不用說翻到10頁往後了。這是沒必要的第一個理由——使用者需求不強烈。
其次,無限分頁的功能對於搜尋引擎而言是非常消耗效能的。你可能感覺很奇怪,翻到第2頁和翻到第1000頁不都是搜尋嘛,能有什麼區別?
實際上,搜尋引擎高可用和高伸縮性的設計帶來的一個副作用就是無法高效實現無限分頁功能,無法高效意味著能實現,但是代價比較大,這是所有搜尋引擎都會面臨的一個問題,專業上叫做「深度分頁
」。這也是沒必要的第二個理由——實現成本高。
我自然不知道Google的搜尋具體是怎麼做的,因此接下來我用ES
(Elasticsearch)為例來解釋一下為什麼深度分頁對搜尋引擎來說是一個頭疼的問題。
Elasticsearch
(下文簡稱ES
)實現的功能和Google以及百度搜尋提供的功能是相同的,而且在實現高可用和高伸縮性的方法上也大同小異,深度分頁的問題都是由這些大同小異的優化方法導致的。
ES
是一個全文搜尋引擎。
全文搜尋引擎又是個什麼鬼?
試想一個場景,你偶然聽到了一首旋律特別優美的歌曲,回家之後依然感覺餘音繞樑,可是無奈你只記得一句歌詞中的幾個字:「傘的邊緣」。這時候搜尋引擎就發揮作用了。
使用搜尋引擎你可以獲取到帶有「傘的邊緣」關鍵詞的所有結果,這些結果有一個術語,叫做檔案。並且搜尋結果是按照檔案與關鍵詞的相關性進行排序之後返回的。我們得到了全文搜尋引擎的定義:
全文搜尋引擎是根據檔案內容查詢相關檔案,並按照相關性順序返回搜尋結果的一種工具
網上衝浪太久,我們會漸漸地把計算機的能力誤以為是自己本身具備的能力,比如我們可能誤以為我們大腦本身就很擅長這種搜尋。恰恰相反,全文檢索的功能是我們非常不擅長的。
舉個例子,如果我對你說:靜夜思。你可能脫口而出:床前明月光,疑是地上霜。舉頭望明月,低頭思故鄉。但是如果我讓你說出帶有「月」的古詩,想必你會費上一番功夫。
包括我們平時看的書也是一樣,目錄本身就是一種符合我們人腦檢索特點的一種搜尋結構,讓我們可以通過檔案ID或者檔案標題這種總領性的標識來找到某一篇檔案,這種結構叫做正排索引
。
而全文搜尋引擎恰好相反,是通過檔案中的內容來找尋檔案,詩詞大會中的飛花令就是人腦版的全文搜尋引擎。
全文搜尋引擎依賴的資料結構就是大名鼎鼎的倒排索引
(「倒排」這個詞就說明這種資料結構和我們正常的思維方式恰好相反),它是單詞和檔案之間包含關係的一種具體實現形式。
打住!不能繼續展開了話題了,趕緊一句話介紹完ES吧!
ES
是一款使用倒排索引資料結構、能夠根據檔案內容查詢相關檔案,並按照相關性順序返回搜尋結果的全文搜尋引擎
高可用是企業級服務必須考慮的一個指標,高可用必然涉及到叢集和分散式,好在ES天然支援叢集模式,可以非常簡單地搭建一個分散式系統。
ES
服務高可用要求其中一個節點如果掛掉了,不能影響正常的搜尋服務。這就意味著掛掉的節點上儲存的資料,必須在其他節點上留有完整的備份。這就是副本的概念。
如上圖所示,Node1
作為主節點,Node2
和Node3
作為副本節點儲存了和主節點完全相同的資料,這樣任何一個節點掛掉都不會影響業務的搜尋。滿足服務的高可用要求。
但是有一個致命的問題,無法實現系統擴容!即使新增另外的節點,對整個系統的容量擴充也起不到任何幫助。因為每一個節點都完整儲存了所有的檔案資料。
因此,ES
引入了分片(Shard
)的概念。
ES
將每個索引(ES
中一系列檔案的集合,相當於MySQL
中的表)分成若干個分片,分片將儘可能平均地分配到不同的節點上。比如現在一個叢集中有3臺節點,索引被分成了5個分片,分配方式大致(因為具體如何平均分配取決於ES
)如下圖所示。
這樣一來,叢集的橫向擴容就非常簡單了,現在我們向叢集中再新增2個節點,則ES
會自動將分片均衡到各個節點之上:
副本和分片功能通力共同作業造就了ES
如今高可用和支援PB級資料量的兩大優勢。
現在我們以3個節點為例,展示一下分片數量為5
,副本數量為1
的情況下,ES
在不同節點上的分片排布情況:
有一點需要注意,上圖範例中主分片和對應的副本分片不會出現在同一個節點上,至於為什麼,大家可以自己思考一下。
ES
是怎麼確定某個檔案應該儲存到哪一個分片上呢?
通過上面的對映演演算法,ES
將檔案資料均勻地分散在各個分片中,其中routing
預設是檔案id。
此外,副本分片的內容依賴主分片進行同步,副本分片存在意義就是負載均衡、頂上隨時可能掛掉的主分片位置,成為新的主分片。
現在基礎知識講完了,終於可以進行搜尋了。
一圖勝千言:
ES
會使用負載均衡策略選擇一個節點作為協調節點(Coordinating Node
)接受請求,這裡假設選擇的是Node3
節點;Node3
節點會在10個主副分片中隨機選擇5個分片(所有分片必須能包含所有內容,且不能重複),傳送search request;Node3
節點;Node3
節點整合5個分片返回的結果,再次排序之後取到對應分頁的結果集返回給使用者端。注:實際上
ES
的搜尋分為Query階段
和Fetch階段
兩個步驟,在Query階段
各個分片返回檔案Id和排序值,Fetch階段
根據檔案Id去對應分片獲取檔案詳情,上面的圖片和文字說明對此進行了簡化,請悉知。
現在考慮使用者端獲取990~1000
的檔案時,ES
在分片儲存的情況下如何給出正確的搜尋結果。
獲取990~1000
的檔案時,ES
在每個分片下都需要獲取1000
個檔案,然後由Coordinating Node
聚合所有分片的結果,然後進行相關性排序,最後選出相關性順序在990~1000
的10
條檔案。
頁數越深,每個節點處理的檔案也就越多,佔用的記憶體也就越多,耗時也就越長,這也就是為什麼搜尋引擎廠商通常不提供深度分頁的原因了,他們沒必要在客戶需求不強烈的功能上浪費效能。
公眾號「蟬沐風」,關注我,邂逅更多精彩文章
完。