1. 程式人生 > >京東億級商品搜尋核心技術解密

京東億級商品搜尋核心技術解密

作者:王春明,現任京東搜尋平臺部負責人,2011年加入京東搜尋團隊,期間一直負責京東搜尋引擎研發工作,主導了多次搜尋架構升級工作保障其滿足京東發展需求,擅長搜尋引擎、高效能服務開發、分散式系統架構。

招聘: 京東搜尋平臺部木有有高階/資深搜尋引擎研發工程師(C/C++)  、高階/資深演算法工程師(C/C++)、高階/資深資料系統工程師(java)等職位,期待您的加入,一起打造彈性搜尋平臺。簡歷投遞至:[email protected],工作地點:北京-北辰世紀中心A座。

京東商品搜尋簡介

京東商品搜尋引擎是搜尋推薦部自主研發的商品搜尋引擎,主要功能是為海量京東使用者提供精準、快速的購物體驗。目前入口主要有PC/移動/微信/手Q搜尋、移動列表頁、店鋪搜尋、店鋪列表等。雖然只有短短几年的時間,系統已經能夠支援日均PV過億的請求,並且經過了多次618店慶和雙11的考驗。

與人們日常使用的如谷歌、百度等大搜索(或稱為“全文搜尋”)引擎相比,京東商品搜尋引擎與前者有相通之處,比如“覆蓋海量資料”、“超高併發查詢”以及“超快速的請求響應時間”,同時又有自身顯著的業務特點:

  • 結構化的商品資料,需要從商品、庫存、價格、促銷、倉儲等多個系統進行抽取;
  • 極高的召回率要求,保證每一個狀態正常的商品都能夠被搜尋到;
  • 商品資訊的及時更新,目的是為了保證使用者極佳的購物體驗——比如不能給使用者展示出下櫃的商品,或者商品的實時價格超出了使用者搜尋限定的範圍。這就要求我們的搜尋引擎要做到和各個系統的資訊時刻保持同步,目前每天更新次數過億;
  • 邏輯複雜的商品業務,需要儲存的商品屬性資訊是倒排索引資訊的2倍之多;
  • 使用者購物的個性化需求,要求系統實現使用者標籤與商品標籤的匹配。

正是由於既要兼顧大搜索引擎的通用需求,同時要契合京東的業務特點,我們將系統架構分為四個部分:1. 爬蟲系統、2. 離線資訊處理系統、3. 索引系統、4. 搜尋服務系統。

為了使各位讀者能夠深入瞭解京東商品搜尋引擎的架構,本文首先介紹了商品搜尋的總體架構,然後依次介紹了爬蟲系統、離線資訊處理系統等各個部分,並且對搜尋技術的最新研究方向做展望,希望對各位讀者有所幫助。

總體架構

京東商品搜尋引擎的整體架構如下圖所示:

京東商品搜尋引擎

從上到下共分為3層。最上層是由搜尋的前端UI層,負責頁面展示。

中間層是由搜尋索引服務、SUG搜尋、相關搜尋、劃詞服務和兜底服務組成。其中,SUG搜尋提供輸入框下拉提示詞功能;相關搜尋提供與query相關的其他搜尋詞服務;劃詞服務提供去除query部分詞的功能;兜底服務用於索引服務異常情況下提供託底,保證使用者基本的搜尋可用。

最下層是索引生產端,主要功能是對接商品、庫存、價格、促銷、倉儲等眾多外部系統,整合相關資料生產全量和增量資料的索引,為線上檢索服務叢集提供全量索引和實時索引資料。

爬蟲系統

商品搜尋引擎的核心是建立商品索引,而建立索引需要詳細的商品資訊資料。我們利用大資料平臺的資料庫抽取介面和中介軟體系統,實現了站內商品爬蟲系統,用來抽取資料庫中的商品資訊和及時發現變化的商品資訊。從實踐的效果上來看,爬蟲系統表現是非常穩定和可靠的。

離線資訊處理系統

離線資訊處理系統主要功能是用來建立商品搜尋引擎的待索引資料,包括全量待索引資料和增量待索引資料。

目前商品全量待索引資料按天進行更新,一部分是商品的基礎屬性資訊,如商品sku、商品名稱、顏色、規格、風格、材質面料等等,屬於比較穩定、短時期內不會變化的資料。另外一部分是商品銷售資訊,如商品銷量、銷售額、評論等,屬於易變資料。這些資料散佈於多個系統中,使用的儲存也各不相同。因此需要對這些來源分散的資料在商品維度進行合併,生成“商品全量待索引寬表”。目前我們建立的全量待索引寬表,不僅應用於搜尋引擎服務,還同時應用於個性化推薦等其他產品服務當中。但是僅生成寬表是無法完成搜尋引擎的索引需求的,因此我們利用Hadoop/MapReduce計算框架對寬表資料進行清洗,並且依照離線業務邏輯規則對資料進行二次“加工”,最終生成一份全量待索引資料。

有些商品資訊,比如“價格”、“庫存”、“上下架”等,經常會產生變化,因此對這些資料做全量索引滿足不了商品搜尋引擎的需求。為了解決資料實時性的強需求,我們建立了增量索引作為全量索引的補充。具體細節上,採用和全量索引類似的方法對資料進行處理,生成增量待索引資料。為了保證增量資料的及時性和準確性,離線資訊處理系統會實時呼叫各商品資訊介面獲取資料,完成增量待索引資料的線上組裝和生產。

索引系統

索引系統是商品搜尋引擎的核心,主要功能是把以商品為維度進行儲存的待索引資料,轉換成以關鍵字為維度進行儲存的資料,用於搜尋引擎上層服務進行呼叫。這裡待索引資料指前面離線資訊處理系統生成的全量待索引資料和增量待索引資料。

此係統對於全量和增量的處理是一致的,唯一的區別在於待處理資料量的差異。一般情況下,全量資料索引由於資料量龐大,採用Hadoop/MapReduce進行;實時資料量小,採用單機進行索引生產。

為了滿足分散式檢索的需求,索引系統還會對索引資料進行分片處理,即按照一定策略將索引資料拆分成較小索引片,用於搜尋服務系統呼叫。

搜尋服務系統

搜尋索引服務系統主要功能是接受使用者請求並響應,返回搜尋結果。搜尋服務系統的發展也經歷了從無到有,從簡單到豐富到過程。主要分為如下幾個階段:

  • 最初,搜尋服務只有1列searcher組成線上檢索服務,能夠完成一些簡單的商品搜尋;
  • 隨著訪問量的增長,搜尋服務系統增加了快取模組,大大加快了請求處理的速度;
  • 接下來為了提高使用者體驗,我們增加了Query Processor服務,負責使用者查詢意圖分析,提升搜尋的準確性。目前Query Processor已經成為了一個融合自然語言處理、機器學習等先進技術的成熟服務,並且還在不斷的進行優化;
  • 為了支援個性化,增加了User Profile服務,負責查詢使用者標籤。將商品的標籤與使用者標籤是否匹配,作為一個特徵加入排序因子,實現搜尋的千人千面;
  • 接著隨著資料量(商品量)的增長,我們將結果包裝功能從檢索服務中獨立出去,成為detail服務(基於快取雲實現的商品資訊KV查詢服務);
  • 將檢索服務進行分片化處理,即採用類似資料庫分庫分表的思想,對商品id,進行hash處理後進行分片,保證各個分片資料均勻。查詢時,將一個搜尋請求分配到多個searcher列上,並行檢索,進行區域性排序後返回給merger。然後merger服務,將多個分片的檢索結果進行歸併,然後再進行業務排序和加工,確定要返回的商品,最後呼叫detail服務包裝,將結果返給給blender。blender將多個搜尋的結果進行融合,返回給前端。需要說明的是,此時搜尋服務系統已經成為了一個“多blender&多Searcher&多merger”的系統。今後無論是訪問量的增長或者資料量的增長,都可以通過擴容來滿足。尤其對於618店慶、11.11之類的峰值搜尋量劇增的情況下,可通過增加每個searcher列伺服器的數量來滿足需求。隨著商品資料的不斷增加,只要適時對資料做更多的分片,相應增加searcher列就可以了。檢索服務分片化機制的建立也標誌著京東搜尋基礎服務系統已經趨於完備。

完整的搜尋索引服務架構,如下圖所示:

搜尋索引

搜尋請求流程如下:

  1. 外部請求通過vip到達blender;
  2. Blender呼叫QP,QP呼叫運營平臺,其中運營平臺主要負責將日常運營資料服務化,QP負責分析query;
  3. Blender同時請求Merger和其他垂直搜尋服務;
  4. Merger呼叫UserProfile獲取使用者標籤資訊;
  5. Merger將請求發給每列searcher;
  6. 每個searcher召回商品並返給Merger;
  7. Merger合併多列searcher的結果,確定需要輸出的商品,請求Datail包裝對應的商品資訊;
  8. Detail包裝商品資訊返給Merger;
  9. Merger將包裝好的商品返給blender;
  10. Blender將merger返回的結果與其他垂直搜尋結果進行合併,最終返回給前端。

Blender、Merger、Searcher和Detail是整個系統的核心元件,它們之間的呼叫關係由Clustermap管理。各個模組將自己的服務註冊到ClusterMap,同時從ClusterMap訂閱其呼叫模組的資訊來確定實際呼叫關係。

簡要搜尋服務流程,如下圖所示(搜尋服務系統內部處理流程):

搜尋服務流程

圖中名詞解釋如下:

  • Page cache:頁面快取,blender模組直接快取輸出的頁面,merger快取了多頁商品id;
  • Attr cache:屬性快取,快取的搜尋屬性導航區的資料;
  • Doc cache:快取查詢詞從全量索引召回的結果;
  • OP:運營平臺服務,負責搜尋運營資料的服務化;
  • QP:query processor,負責query意圖識別。

使用者請求傳送到blender,首先解析引數。如果命中blender page cache直接返回給使用者。如果沒有命中,則呼叫運營平臺服務(OP)和QP,並將其傳給Merger,Merge會檢查是否命中Attr cache,如果命中並且恰好僅請求屬性彙總結果,直接返回給blender。否則進一步檢視是否命中merger page cahce,如果命中直接呼叫detail包裝,返給blender。如果沒有命中,則呼叫User Profile獲取使用者標籤,將其傳給searcher(篇幅所限,圖中只列了一個searcher,實際是多個)。Searcher接到請求,判斷是否命中doc cache,如果命中doc cache,則拉取增量結果;如果沒有命中doc cahe,則拉取全量和增量結果。然後依次進行排序、線上業務處理,把結果返給merger。Merger合併多個searcher結果,排序、線上業務處理,最後呼叫detail包裝,最後將結果返給blender,blender合併多個搜尋結果後返回給使用者。

作為一個高併發系統,為了保證高召回率和低響應延時,我們把整個搜尋服務流程的處理全部放在記憶體當中進行計算。多個searcher併發處理請求,同時單個searcher內部採用執行緒池技術,即所有執行緒之間共享倒排索引和商品屬性資訊,提高記憶體使用效率;每個查詢使用一個獨立執行緒序列執行,保證併發的多個查詢執行緒之間互不影響。此外通過合理的設定執行緒池的大小,我們可以保證系統的CPU資源得到充分利用。在上述兩個方面對系統進行優化之後,整個搜尋服務系統的穩定性、召回率、記憶體使用率、計算速度等指標都有大幅度的提高。但是我們改進系統的步伐並沒有停歇,因為通過實踐發現基於記憶體和執行緒池的搜尋服務仍然有幾個瓶頸點亟需解決,主要包括:拉取倒排、排序和線上業務處理。針對這些問題,我們進行了二次優化,主要包括如下措施:

1. 多級快取策略

  1. Blender Page cache:由於搜尋符合網際網路的二八法則,20%熱門查詢頻度非常高,佔每天搜尋請求量80%。針對這一特點,搜尋第一級快取以查詢請求為key,將返回給使用者的頁面作為value。對於完全相同的請求,直接從快取返回結果。頁面快取策略上線伊始,快取命中率就接近了30%,基本解決了當時的效能問題。
  2. Merge Page cache:隨著業務的發展,排序結果需要針對不同使用者實現個性化訂製,這就導致請求中會包含使用者的user pin。如果直接將user pin放入快取作為key,會導致blender cache的key數量暴增,不但需要超大的快取空間,同時快取的命中率也會極低,最終會導致線上個性化服務的體驗滿意度降低。為了解決這個問題,將user_pin加入key,但是value只儲存排序好的商品id,這樣需要的快取空間遠遠小於blender cache。當命中快取後,呼叫detail直接進行結果包裝。為了進一步提高快取命中率,利用使用者搜尋的翻頁習慣,即離線統計出使用者的翻頁數TP99,然後在value中快取這些頁面涉及到所有的商品id,從實踐效果來看,使用者後續的翻頁請求大部分會命中cache。
  3. 在深入分析了業務和排序的需求之後,我們發現拉取倒排的結果只和“查詢詞&篩選條件”有關,而與使用者無關,因此可以按照“查詢詞&篩選條件”作為key的方式對其進行快取。

雖然拉取倒排結果快取的key很快就解決了,但是我們在解決Value的儲存時遇到了兩個問題:1)拉取倒排的結果非常之多,導致快取過大;2)對此結果快取,會降低實時索引的時效性。

對於問題1),在分析了業務之後,對需要快取的資訊進行了大量的精簡併採用壓縮儲存,最終將一個查詢的快取控制在0.5M以下。

對於問題2),我們將拉取倒排結果分為兩部分,第一部分是從全量索引拉取倒排的結果,第二部分是從實時索引拉取倒排的結果。為了和全量索引的更新頻率保持同步,我們把第一部分資料進行快取的週期置為1天。對於第二部分資料,由於增量結果遠遠少於全量結果(一般增量只有全量5%不到),每次快取都進行實時計算,這就是圖3中的doc cache機制。從實踐中來看,命中doc cache的響應時間比未命中的降低了1-2個數量級。將來隨著增量結果的積累,如果實時拉取倒排結果成為效能瓶頸,可以對增量索引分段也進行快取。

2. 截斷策略

對於有些熱門查詢,由於其結果較多,比如“男裝”、“鞋”之類的query,原始查詢結果幾千萬個,如果對這些結果挨個進行處理,效能會非常差。同時,從使用者角度分析,一個查詢只有排在最前面的結果對使用者才有意義。通過分析使用者翻頁次數,可以得到截斷保留topN結果。如何保證截斷不影響使用者體驗呢?首先我們對商品建立離線模型,即為每個商品計算出一個質量分資料。然後在索引階段,將所有商品按照質量分降序排列,保證在倒排鏈中,排在前面的商品質量分總是高於後面的。線上從前往後拉取倒排過程中,如果結果數達到10*topN時,停止拉取倒排。隨後對結果計算文字相關性,再按照文字相關性取topN個。截斷演算法上線前後,雖然KPI指標無明顯變化,但是對大結果查詢效能提升了一個數量級。

3. 均勻分片策略

從總體架構圖中我們可以看到,如果我們將一個term的倒排鏈進行均分,那麼相應term的拉取倒排也會被分配至各個searcher列。正是由於各個searcher列是平行計算的,這樣的均分操作就可以大大減少每個查詢的平均響應時間。從理論上來講,我們採用的均勻分片策略,也有效的契合了拉取倒排、排序、線上業務處理等CPU密集型的任務。但是分片增加,會帶來硬體成本增高的後果,同時叢集節點間的通訊成本也會增加,需要進一步權衡折衷。

4. 業務優化

京東的搜尋業務並不只有上面所述的策略和工程邏輯,還必須融合很多業務邏輯。由於每一次搜尋幾乎都會召回很多結果,如果業務邏輯處理不好,也會導致搜尋體驗不好。針對這一問題並沒有通用的解決方法,但是通過實踐我們總結出一個基本原則:在離線階段完成儘可能多的業務邏輯,減少線上計算量!例如進行搜尋排序時,我們需要根據使用者搜尋歷史行為(瀏覽、點選、購買等)對召回的結果進行排序上的調整,在工程實現上我們會先離線統計出同一個query下所有使用者對每個展示商品的行為,然後建立模型,計算出該query下每個商品的權重,將其以hash結構儲存;線上排序時,直接以query+商品id為key,取出權重作為反饋特徵參與綜合排序。

搜尋技術的新發展

我們在當前的架構基礎之上,正在進行一些新的探索,比如場景搜尋和影象搜尋。

場景搜尋

隨著目前京東集團的業務的擴充套件,使用者在使用搜索時,目的不僅僅是查詢商品,還可能查詢促銷活動資訊。為了滿足這些新的需求,我們在目前商品索引融合了促銷系統的資料。我們首先在Query Processor中增加對應意圖的識別,然後將促銷等資料轉換為索引資料。只要Query Processor識別出使用者提出這方便的查詢意圖,將對應的結果返回。

影象搜尋

傳統搜尋僅僅針對文字,但是電商系統的商品圖片非常重要,很多購買決策依賴於它。目前我們利用deep learning技術離線訓練圖片特徵,並將其做成索引。當用戶使用實拍圖或者網圖來搜尋時,採用相同的方式提取特徵,然後從索引中召回最相似商品返回給使用者。

文章出處:開濤的部落格(訂閱號ID:kaitao-1234567)