天天看點

閑魚如何高效打造一個多業務、低侵入的搜尋鍊路

問題及現狀

閑魚搜尋很多場景基于集團搜尋中台能力,縱觀閑魚搜尋鍊路,存在多角色(工程、算法工程、算法等)、多業務(閑魚無憂購、租房、文章等)、多節點(離線資料源聚合、線上召回、URF Rank等),具有明顯的複雜性。并且閑魚主搜僅存在一條鍊路支援搜尋多業務發展,各角色、各業務、各節點處于高耦合串行疊代模式。在大資料量、多業務、多角色并行場景下,以下問題日益明顯: 

1、疊代效率低、排期長,無法滿足新業務快速疊代訴求:主要展現在資料量大,單次疊代周期長,以及多業務、多角色串行操作,耦合嚴重; 

2、風險高:不同業務、不同特性均在主引擎召回鍊路執行修改,侵入大,風險高; 

3、幹預能力弱,業務間混排能力不足:缺少幹預扶持能力,無法有效為創新業務提供定制化孵化能力。

閑魚一次請求完整流程如下:

閑魚如何高效打造一個多業務、低侵入的搜尋鍊路

注:QP,即Query Planner,主要用于預測使用者query搜尋意圖。

SP在測試環節下調用拓撲示例如下:

閑魚如何高效打造一個多業務、低侵入的搜尋鍊路

關鍵節點:

blender是SP服務核心入口,主要職責包括解析使用者query意圖、引擎分頁召回、搜尋資料補齊等功能。

uniq_session是召回核心入口,通過引擎召回、分頁處理、URF Rank等操作,擷取最終商品清單 

ha3_searcher是引擎召回入口,翻譯使用者query為HA3引擎查詢query,請求引擎線上服務,召回滿足條件商品資訊 

uniq_summary是商品資訊補充入口,依據商品id清單,補齊商品詳細資訊并傳回,最終在搜尋結果頁呈現給使用者

背景概述

閑魚搜尋很多場景基于集團搜尋中台能力,搜尋引擎分為資料源聚合(離線dump)、全量/增量/實時索引建構及線上服務等部分,通過集團内部一系列處理階段,對客戶提供高可用高性能的搜尋服務。服務架構如下:

閑魚如何高效打造一個多業務、低侵入的搜尋鍊路

其中:

SP/SPL是集團内一套建構于Wunder上的開發工具,提供開發測試打包上線的視圖界面以及一套業務函數庫。HA3是一套基于suez架構的全文檢索引擎,提供豐富的線上查詢子句,過濾子句,排序子句,聚合子句且支援使用者自定義開發排序插件。

Qrs用于接收使用者查詢,将使用者查詢分發給Searcher,收集Searcher傳回的結果作整合,最終傳回給使用者。Searcher是搜尋查詢的執行者,主要包括反向索引召回、統計、條件過濾、文檔打分及排序及摘要生成。在實際的部署中,Qrs和Searcher都是采用多行部署的方式,可以根據業務的流量變化作調整。Searcher還可以根據業務的資料量調整列數,解決單機記憶體或磁盤放不下所有資料的問題。

剖開HA3引擎,我們可以粗略看下引擎側索引的建構以及線上召回鍊路:

閑魚如何高效打造一個多業務、低侵入的搜尋鍊路

藍色部分表示HA3線上服務,提供線上召回商品服務;綠色部分表示引擎資料源聚合(離線dump)邏輯,将商品資訊建構成對應的引擎資料源。下圖為一個離線引擎dump的資料源圖示例:

閑魚如何高效打造一個多業務、低侵入的搜尋鍊路

設計思路

由于我們要解決的問題是如何安全、靈活、高效地支撐多業務接入閑魚搜尋,并提供不同業務間混排能力,我們的設計想法如下:

1、支援業務間互相隔離,從引擎側深層次隔離,提高整體疊代效率

2、支援多引擎并發召回能力,解決搜尋RT惡化問題

3、支援多引擎召回結果合并能力,并統一傳遞給URF Rank執行混排

4、支援實時幹預能力,滿足不同業務孵化訴求

調研集團内部已有實作模式,并結合閑魚搜尋業務發展訴求,我們重新設計并執行閑魚底層引擎召回邏輯更新流程,從單引擎單路召回架構更新為多引擎多路并發召回架構。

閑魚如何高效打造一個多業務、低侵入的搜尋鍊路

Search Planner及搜尋引擎層更新如下圖所示:

閑魚如何高效打造一個多業務、低侵入的搜尋鍊路

主要更新節點如下:

1、步驟1中解析使用者query,擷取多引擎辨別,解析并緩存引擎配置資訊 

2、步驟3流控子產品從配置中心擷取引擎定制化配置,并執行query改寫邏輯,生成對應的HA3請求串 

3、步驟4依據query中攜帶的多引擎辨別,并發請求不同引擎線上服務召回商品,并合并召回結果,統一調用步驟5的精排服務能力 

4、步驟5算法側更新支援多引擎結果,支援多業務混排能力

實作方案

依照上節的設計思路,實作方案核心主要關注以下幾點:

1、如何解析使用者query,并緩存使用者query相關引擎配置資訊 

每一個引擎均存在唯一的辨別id(便于說明:主搜引擎辨別為0,閑魚無憂購引擎辨別為1,向量引擎辨別為2),上層搜尋業務層依據不同業務場景組裝引擎查詢query,包括組裝引擎辨別清單(比如bizType=0,1,2,表示同時從主搜引擎、閑魚無憂購引擎、向量引擎召回商品)。

Search Planner核心解析邏輯如下:

-- 建構請求的biz清單
function get_search_biz_type_list(query, param) 
    -- 搜尋biz配置清單
    local search_biz_type_list = {}
    if query.bizType then
        if type(query.bizType) == "string" then
            table.insert(search_biz_type_list, query.bizType)
        else 
            search_biz_type_list = query.bizType
        end
    end

    if search_biz_type_list == nil or #search_biz_type_list < 1 then
        table.insert(search_biz_type_list, param.default_biz_type)
    end

    local search_list = {}
    if not query._enable_multiplexed then
        table.insert(search_list, {biz = param.default_biz_type, biz_type = search_biz_type_list})

        return search_list
    end

    for _, type in ipairs(search_biz_type_list) do
        table.insert(search_list, {biz = type, biz_type=type})
    end

    return search_list
end
           

2、流控子產品如何擷取引擎定制化配置,執行query改寫邏輯 

目前流控子產品通過阿裡巴巴集團内部廣泛使用的diamond配置中心擷取詳細配置參數,并與引擎唯一辨別綁定映射關系。Diamond是淘寶内部廣泛使用的配置中心,提供持久化管理和動态配置推送服務。采用推送服務,每次請求不再需要實時從遠端擷取配置,降低搜尋RT,提高搜尋體驗。同一環境下,Diamond中通過唯一的GroupId + DataId辨別參數配置。

function parse_diamond_config(query, param)
    query._cluster_info = {}

    -- 多引擎配置
    local cluster_info_str = param.cluster_info
    local cluster_info = setup.split(cluster_info_str, ";")

    if not cluster_info then
        return
    end

    for _, info in ipairs(cluster_info) do
        local single_cluster_info = setup.split(info, ":")

        if single_cluster_info and #single_cluster_info > 1 then
            query._cluster_info[single_cluster_info[1]] = single_cluster_info[2]
        end
    end

end
           

query改寫邏輯封裝在不同的函數組中,通過前置分支判斷,調用函數組中不同改寫入口,生成定制化query。

-- 改寫向量召回query
function rewrite_embedding_recall(query, switch, param)
  -- 建立query
  local que = {}
  que._biz = query._biz
  que._cluster_name = query._cluster_name

  que.q = "NULL"

  if really_need_search_embedding_recall(query) then
    local qp_idlefish_table_qp_reserve_str_4rs_query_vector = query._qp_idlefish_table_qp_reserve_str_4rs_query_vector
    local q_str = qp_idlefish_table_qp_reserve_str_4rs_query_vector .. "&n=" .. param.embedding_recall_search_hit

    que.q = "sim_vec:" .. url_encode(q_str)
  end

  -- 改寫attribues
  if not switch.full_phase then
    que.attribute = "item_id,bizType"
  end

  -- 改寫rank
  que.rank = { -- rank 子句
   rank_profile = 'RecallScorer'
  }

  return que
end
           

3、如何并發執行多引擎召回、合并流程,預防搜尋RT惡化 

借助于SPL異步協程能力,通過一條lambda函數,實作并行調用,以解決搜尋RT惡化問題。如:value = search_http("/qp?xxx"):value(),即表示一次異步并發調用操作。

-- 解析入口
function multi_parse(res_vec)
    local items_search_lua_tables = {}

    for _, p in ipairs(res_vec) do
        -- p.res:value()執行異步并發請求
        local res = p and p.res and p.res:value()
        local biz = p and p.biz

        ... ...

        local ret_table = res:getTable()

        table.insert(items_search_lua_tables, {biz=biz, tbl = ret_table})
    end
end
           

通過多引擎隔離、query改寫手段,各業務間能有效地解耦,滿足各業務定制化訴求,并且對閑魚主搜服務能力幹擾最小,将問題集中于單業務内;

通過內建dianmond配置中心的流控子產品,達到實時幹預query搜尋能力,并有效孵化創新業務,如目前閑魚無憂購創新業務;

通過多引擎并發召回及合并能力,一是解決搜尋RT惡化問題,二是結合URF Rank,有效控制不同業務間混排效果,并最終呈現給使用者,以達到更好的使用者體驗。

以一個精簡版使用者query為例,傳遞給sp的query如下(查詢關鍵字為“Zimmermann 裙”,從三個引擎(0、1、2)中召回結果):

/bin/sp?outfmt=json2&src=idlefishwireless&app=spl_secondHand_new&q=Zimmermann%20%E8%A3%99&isForbiden=0&wlsort=35&s=0&n=10&enable_rank=true&enable_advsort=true&new_rs=true&ha3_version=v2&sellStatus=0&auctionType=a,b&se=tis2&bizType=0,1,2
           

對應可視化調用拓撲如下:

閑魚如何高效打造一個多業務、低侵入的搜尋鍊路

multi_searcher是多路召回入口,包括并發多引擎召回、多引擎結果解析、合并等邏輯。

紅框中每一個ha3_searcher表示一條獨立引擎召回鍊路,圖中三條鍊路可得出目前召回是多路并發召回(56ms < 54ms + 10ms +20ms)。

效果

目前多引擎多路召回能力已經在閑魚無憂購引擎、向量引擎場景下落地,整體取得了不錯的效果: 

1、建構快 

主引擎中閑魚無憂購商品引擎dump大全量單次建構平均時長約為14h,遷移獨立引擎後平均建構時長約為5h 

2、接入快 

主搜場景接入向量召回能力,采用目前多路召回能力,召回鍊路打通原先1周縮短到2天

3、體驗好 

主搜場景開啟主搜引擎、閑魚無憂購引擎、向量引擎三路并發召回能力,整體RT上漲約15ms

4、資源省 

主引擎占用記憶體(21.6T),無憂購引擎(210M),向量引擎(20G) 

5、維護成本低 

無憂購引擎整體12個schema字段,向量引擎目前隻需要2個字段

閑魚無憂購遷移獨立引擎,整體曝光PV提升近50%,支付訂單整體提升近33%。

閑魚無憂購商品與主引擎C2C商品混排效果如下:

閑魚如何高效打造一個多業務、低侵入的搜尋鍊路

展望

前期主要集中于搜尋架構更新快速落地,快速支撐業務發展訴求,目前實作上存在不優雅、使用者使用門檻高等問題,後續我們将圍繞多路召回能力及使用者體驗繼續優化:

1、隔離query改寫及多引擎合并能力,将職責從平台側遷移到業務側,提高主鍊路穩定性,以及業務疊代效率 

目前各個引擎query改寫能力及合并能力,和主鍊路流程揉合在一起,未做到垂直業務間、垂直業務與主鍊路間隔離,對主鍊路的穩定性存在一定的影響 

2、精細化流控及幹預能力,并提供可視化配置能力 

目前流控及幹預手段,統一使用集團diamond配置,缺少一個便于操作使用的可視化能力

3、SP功能核心代碼邏輯從lua遷移java,降低使用者學習、開發成本 

新的一年,閑魚側将有更多的垂直業務接入搜尋,多路召回能力将極大的幫助更多垂直業務快速接入,并提供更優雅、靈活、高效的業務混排能力,為各業務的持續發展提供強有力的支撐。