<ins id="jxy61"><option id="jxy61"><menu id="jxy61"></menu></option></ins>
          1. 煉數成金 門戶 大數據 查看內容

            Pinterest 搜索系統實時化的挑戰和建設實踐

            2021-4-6 11:22| 發布者: 煉數成金_小數| 查看: 5981| 評論: 0|原作者: 王強 譯|來自: InfoQ

            摘要: Pinterest 的內部搜索引擎 Manas 是一個通用的信息檢索平臺。正如我們在上一篇文章中討論的那樣,Manas 被設計為兼具高性能、可用性和可伸縮性的搜索框架。如今,Manas 支持大多數 Pinterest 產品的搜索功能,包括廣 ...
            Pinterest 的內部搜索引擎 Manas 是一個通用的信息檢索平臺。正如我們在上一篇文章中討論的那樣,Manas 被設計為兼具高性能、可用性和可伸縮性的搜索框架。如今,Manas 支持大多數 Pinterest 產品的搜索功能,包括廣告、搜索、Homefeed、Related Pins、Visual 和 Shopping。

            搜索系統的關鍵指標之一是索引延遲,也就是更新搜索索引以反映更改所花費的時間。隨著我們系統的功能不斷增加,新的用例持續引入,即時索引新文檔的能力變得越來越重要。Manas 之前已經支持了增量索引,能夠提供數十分鐘數量級的索引延遲。不幸的是,這還不能滿足我們來自廣告和 following feeds 持續增長的業務需求。我們決定在 Manas 中構建一個新模塊,以進一步將索引延遲減少到幾分之一秒的水平。

            在這篇博客文章中,我們描述了這一系統的架構及其主要挑戰,并介紹了我們所做權衡的細節內容。

            本文由 Michael Mi 發表在 medium.com,經授權由 InfoQ 中文站翻譯并分享

            挑戰
            新的需求伴隨著新的挑戰。以下是我們面臨的幾個主要挑戰。

            索引延遲
            對于 Lucene、Vespa 等開源項目來說,小批(tiny batch)方法(又稱近實時)是更受歡迎的選擇。使用這種方法,只有在調用索引提交時才可以搜索新編寫的文檔。結果,你需要在索引延遲和吞吐量之間進行權衡。不幸的是,我們無法利用這種方法將索引延遲減少到幾秒鐘級別。

            索引刷新能力
            實時服務的缺點之一是缺乏索引刷新敏捷性。對于一個批處理管道來說,重新運行索引作業以立即獲取所有模式更改是很簡單。但當涉及到實時服務管道時,實現高效的索引刷新支持就是一件很復雜的事情了。

            為不斷變化的數據實現擴展
            為了避免過度配置,系統采用了自動縮放以根據實際查詢負載來調整副本。如果索引是不可變的,那么新副本創建起來就相對容易:你只需將索引復制到新節點即可。困難之處在于處理不斷變化的索引:如何確保所有副本都具有相同的索引?

            錯誤恢復
            Manas 是一項數據密集型服務,其中每臺主機可提供的索引高達數百 GB。Manas 也是一個有狀態的系統,一個錯誤的二進制文件可能會導致連回滾都無法解決的數據問題。我們需要構建一個同時支持容錯和錯誤恢復的系統,以便從二進制錯誤和數據損壞中恢復。

            從靜態到實時

            我們來簡要介紹一下常規靜態服務和實時服務之間的區別。如上圖所示,實時服務的主要工作是將索引管道從離線遷移到在線。

            對于靜態服務,索引是通過一個批處理工作流離線生成的,然后將它們復制到 Leaf 用以在線服務。對于批處理工作流,由于高昂的框架開銷,幾乎不可能在幾分之一秒內建立可服務的索引。實時服務不是使用脫機工作流,而是在服務中即時處理所有寫入。此外,實時索引管道用的是與靜態索引管道相同的索引格式來處理寫入,從而使我們能夠重用整個索引讀取邏輯。記住這一點,我們來繼續了解實時服務的工作機制。

            索引接口
            我們不是直接使用 RPC,而是使用了 Kafka 作為我們的高寫入吞吐流。Leaf 服務器不斷拉取突變以建立增量索引。事實證明,這一決策以多種方式極大簡化了我們的系統:
            數據復制和寫入失敗由 Kafka 負責。
            借助回查能力,Kafka 隊列也可以用作 WAL。
            在每個分區中都有嚴格的順序保證,系統可以隨意應用刪除操作,而不必擔心正確性。

            架構概述
            由于服務邏輯可以通過共享索引格式重用,因此我們將重點放在索引數據流上。

            本質上,實時 Manas leaf 是一個 LSM 引擎,它將隨機 IO 寫入轉換為順序 IO,并為讀取放大和寫入放大應用程序提供高效的服務。如下所示,整個索引流程包括三個關鍵步驟。我們來一一討論。

            實時段構建
            除了現有的靜態段(segment)外,我們還引入了實時段。如上所示,系統中有兩種實時段:活動實時段和密封(sealed)實時段。

            活動實時段是可變的組件,用于累積從 Kafka 拉取的突變(添加 / 刪除)。值得一提的是,將一個文檔添加到一個實時段后,在文檔級別提交后即可立即搜索。一旦活動實時段達到一個可配置的閾值,它就會被密封,轉為不可變并放入一個刷新隊列中。同時,系統創建了一個新的活動實時段以繼續累積突變。

            在服務重啟的情況下,可以通過重播來自 Kafka 的消息來重建各個實時段。

            索引刷新
            索引刷新是將內存中的數據從一個實時段持久存儲到一個壓縮索引文件中的過程。當一個實時段被密封時將自動觸發一次刷新,并且還可以使用調試命令手動觸發刷新。

            索引刷新是一種有益的運算符,可確保數據持久性,這樣我們就無需在重新啟動期間從頭開始重建內存中的段。此外,通過壓縮的不可變索引,刷新減少了一個段的內存占用,并提高了服務效率。

            索引壓縮
            隨著時間的流逝,生成的多個小段會影響服務性能。為了克服這個問題,我們引入了一個后臺壓縮線程來將這些小段合并為更大的段。由于刪除運算符只是將文檔標記為已刪除,而不是物理刪除它們,因此壓縮線程還會保留這些已刪除 / 過期的文檔。

            在每個刷新和壓縮運算符之后,將生成一個由所有靜態段組成的新索引清單。一些 Kafka 偏移量(用作檢查點)也被添加到每個清單中。根據這些檢查點,服務就能知道重新啟動后在哪里消費消息。

            設計細節
            在本節中,我們將更具體地介紹幾個關鍵領域。我們從最有趣的部分開始,即并發模型。

            并發模型
            如前所述,實時段是我們需要同時處理讀取和寫入操作的可變組件。不幸的是,那些開源項目采用的近實時方法無法滿足我們的業務需求。相比之下,我們選擇了另一種方法,使我們能夠在添加到索引后立即提交文檔,而無需等待索引刷新。為了提升性能,我們針對數據結構采用了一個無鎖技術,以適應我們的使用狀況,F在來深入到細節吧!

            實時段
            每個實時段都包含一個倒排索引和一個正排索引。倒排索引在邏輯上是從 term 到發布列表(用于檢索的文檔 ID 列表)的映射。同時,正排索引存儲一個用于完整評分和數據提取的任意二進制 Blob。我們只關注實時倒排索引部分,與正排索引相比,它更有趣且更具挑戰性。

            從高層次上講,實時段和靜態段之間的主要區別是可變性。對于實時倒排索引,從 term 到發布列表的映射必須是并發的。folly 的并發哈希圖等開源項目為此提供了很好的支持。我們更關心的是發布列表的內部表示,它可以有效地支持我們的并發模型。

            僅附加向量
            一般來說,單寫入者 / 多讀取者模型效率更高,推理起來也更容易。我們選擇了與 HDFS 類似的數據模型,它具有僅附加的無鎖數據結構。我們來仔細研究一下讀取者和寫入者之間的互動方式。

            寫入者將文檔 ID 附加到向量中,然后提交大。╯ize)以使讀取者可以訪問它
            讀取者在訪問數據之前獲取一個快照(較大到提交的大。

            為了避免隨著發布列表的增長而產生的內存復制開銷,我們在內部將數據作為一個存儲桶列表來管理。當我們的容量用完時,只需添加一個新的存儲桶即可,無需接觸舊的存儲桶。另外,通常搜索引擎使用跳過列表來加快跳過運算符的速度。由于采用了這種格式,我們可以方便地支持一個單級跳過列表,這對于實時倒排索引已經足夠了,因為它的大小通常很小。
             
            文檔原子性
            現在有了僅追加的向量,我們就可以實現單個發布列表的原子性。但是,文檔可以包含一個 term 列表,并且我們最終可能會返回帶有部分更新索引的意外文檔。為了解決這個潛在的問題,我們引入了一個文檔級別提交,以保證文檔的原子性。在服務管道中使用了一個額外的過濾器來確保僅返回已提交的文檔。

            說到文檔原子性,文檔更新是這里值得一提的另一種情況。對于每次文檔更新,我們特意將其轉換為兩個運算符:添加新文檔,然后從索引中刪除舊文檔。盡管每個運算符都是原子的,但加在一起我們就不能保證原子性了。我們認為可以在很短的時間窗口內返回舊版本或新版本,但盡管如此,我們還是在服務管道中添加了重復數據刪除邏輯,以在同時返回新舊版本時過濾掉舊版本。
             
            寫縮放
            一個自然而然的問題是,如果你的數據結構僅支持一次寫入和多次讀取并發模型,那么如果單個線程不能及時處理所有寫入操作該怎么辦?盲目添加更多分片只是為了擴展寫入吞吐量,這似乎不是一個好主意。雖說這是一個確實存在的擔憂,但在我們的設計中已經考慮到了這一點。

            用于數據結構的一次寫入和多次讀取并發模型并不意味著我們不能使用多個線程進行寫入。我們計劃使用 term 分片策略來支持具有多個線程的寫入。如上圖所示,對于具有 term 列表的給定文檔,每個 term 將始終映射到固定線程,以便為單次寫入和多次讀取定制的所有數據結構都可以無限制地直接重用。

            索引刷新
            索引刷新功能是我們產品的一項關鍵特性,可實現快速周轉并提高開發速度。一般可以使用兩種方法以高效刷新索引,它們分別是動態回填和從離線構建的索引恢復。
             
            回填索引
            我們提供了以合理的吞吐量回填文檔的功能。為了避免影響生產的新鮮度,我們需要一個優先級較低的單獨流來處理回填流量。結果,兩個流中可能會存在文檔的兩個版本,而舊版本將覆蓋新版本。為了克服這個問題,我們需要在實時索引管道中引入一種版本控制機制和一個沖突解決程序,以決定哪個版本更新鮮。
             
            從離線構建索引中恢復
            有時,以給定的速度對整個數據集進行回填會非常耗時。我們支持的另一種更快的索引刷新方法是離線構建索引,然后使用離線構建索引和 Kafka 流之間的同步機制來從離線索引中恢復索引。

            故障轉移和自動擴展
            出于各種原因,我們有時會需要啟動新實例,例如故障轉移和自動縮放等。對于靜態服務,使用從索引存儲下載的不變索引來啟動新實例是很容易的。

            但是,對于具有不斷變化的索引的實時服務而言,這就變得很復雜了。我們如何確保新實例最終具有與其他實例相同的索引副本呢?

            我們決定使用基于 Leader 的復制,如上圖所示。

            我們的流程如下所示:
            Leader 定期拍攝新快照并將其上傳到持久索引存儲中
            默認情況下,新實例從索引存儲下載的快照
            新實例根據快照索引中的檢查點恢復消費來自 Kafka 的消息
            一旦新實例趕上進度,便開始為流量提供服務

            這種設計中有一些關鍵點值得一提:

            Leader 選舉
            Leader 的職責是拍攝快照并定期上傳索引。這意味著我們可以在較短的時間內(最多幾個小時)無 Leader 或有多個 Leader。因此,我們在選擇 Leader 選舉算法方面具有一定的靈活性。為簡單起見,我們選擇使用集群維護作業來靜態地選舉一個 Leader,在此我們會定期檢查我們是否有一個好的 Leader。
             
            快照上傳
            通常,新實例僅連接到 Leader 以下載快照。在這種方法中,從新實例下載快照可能會使 Leader 過載,從而導致級聯故障。相反,我們選擇將快照定期上載到索引存儲,犧牲存儲空間和新鮮度以保持穩定性。此外,上載的快照對于錯誤恢復很有用,稍后將對此介紹。

            錯誤恢復
            如上所述,錯誤恢復是實時服務系統的另一挑戰。我們需要處理一些涉及數據損壞的特定場景。
             
            輸入數據損壞
            我們使用 Kafka 作為輸入寫入流;不幸的是,這些消息是不可變的,因為生產者只能在其上附加消息,而不能更改現有消息的內容。這意味著一旦將數據損壞引入 Kafka 消息中,它將是永久性的。多虧了上傳的快照,我們能夠將索引回退到不損壞的狀態,跳過損壞的消息,然后使用這個修復來消費新消息。
             
            二進制錯誤導致數據損壞
            盡管我們擁有成熟的靜態集群索引驗證管道,以確保在換入新版本之前新索引和新二進制文件均不會出現問題,但仍有一些錯誤會潛入生產環境。幸運的是,我們可以通過回滾二進制或索引來解決此問題。對于實時服務而言,回滾二進制文件無法回滾索引中的錯誤,這帶來了更大的麻煩。使用快照上傳機制,我們可以將二進制文件與回退的索引一起回滾,然后從 Kafka 重放消息以修復索引中的錯誤。

            下一步計劃
            隨著越來越多的場景加入 Manas,我們需要不斷提高系統的效率、可伸縮性和能力。我們路線圖中的一些有趣的項目如下:
            共同托管靜態和實時集群以簡化我們的服務棧
            優化系統以支持大型數據集
            構建一個基于通用嵌入的檢索以支持高級場景

            致謝:這篇文章總結了幾個季度的工作,涉及多個團隊。感謝 Tim Koh、Haibin Xie、George Wu、Sheng Chen、Jiacheng Hong 和 Zheng Liu 的無數貢獻。感謝 Mukund Narasimhan、Angela Sheu、Ang Xu、Chengcheng Hu 和 Dumitru Daniliuc 所做的許多有意義的討論和反饋。感謝 Roger Wang 和 Randall Keller 的出色領導。

            原文鏈接:
            Manas Realtime — Enabling changes to be searchable in a blink of an eye
            https://medium.com/pinterest-engineering/manas-realtime-enabling-changes-to-be-searchable-in-a-blink-of-an-eye-36acc3506843

            聲明:文章收集于網絡,版權歸原作者所有,為傳播信息而發,如有侵權,請聯系小編刪除,謝謝!

            歡迎加入本站公開興趣群
            軟件開發技術群
            興趣范圍包括:Java,C/C++,Python,PHP,Ruby,shell等各種語言開發經驗交流,各種框架使用,外包項目機會,學習、培訓、跳槽等交流
            QQ群:26931708

            Hadoop源代碼研究群
            興趣范圍包括:Hadoop源代碼解讀,改進,優化,分布式系統場景定制,與Hadoop有關的各種開源項目,總之就是玩轉Hadoop
            QQ群:288410967 

            鮮花

            握手

            雷人

            路過

            雞蛋

            相關閱讀

            最新評論

            熱門頻道

            • 大數據

            即將開課

             

            GMT+8, 2021-4-16 13:52 , Processed in 0.129132 second(s), 25 queries .

            年轻人手机在线观看