写点什么

闲鱼如何高效打造一个多业务、低侵入的搜索链路

  • 2021-03-27
  • 本文字数:4475 字

    阅读完需:约 15 分钟

闲鱼如何高效打造一个多业务、低侵入的搜索链路

问题及现状

闲鱼搜索很多场景基于集团搜索中台能力,纵观闲鱼搜索链路,存在多角色(工程、算法工程、算法等)、多业务(闲鱼无忧购、租房、帖子等)、多节点(离线数据源聚合、在线召回、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_listend
复制代码


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。


-- 改写向量召回queryfunction 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 queend
复制代码


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}) endend
复制代码


通过多引擎隔离、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,降低用户学习、开发成本 

新的一年,闲鱼侧将有更多的垂直业务接入搜索,多路召回能力将极大的帮助更多垂直业务快速接入,并提供更优雅、灵活、高效的业务混排能力,为各业务的持续发展提供强有力的支撑。


本文转载自:闲鱼技术(ID:XYtech_Alibaba)

原文链接:闲鱼如何高效打造一个多业务、低侵入的搜索链路

2021-03-27 07:003509

评论

发布
暂无评论
发现更多内容

内卷

Jxin

中国式美好假期:用AI地图,抢先体验未来出行

脑极体

数据架构:数据冷热分离实践思考

程序员架构进阶

数据架构 架构设计 28天写作 5月日更 冷热分离

AI英雄出少年!奔赴星辰,他们正在创造黄金时代

百度大脑

AI

私域流量这件事,古代就有了……

白洞计划

Nginx-代理服务器

进击的梦清

nginx Docker Linux Docker-compose 代理

华仔架构训练营作业(模块三)

不听不听王八念晶

基于OpenPAI细化部署 Hadoop 集群

Damon

hadoop 5月日更

区块链+农业,如何升级农业价值链

CECBC

农业

第三次作业

Geek_9cf7b5

深入浅出 LVS 负载均衡系列(二):DR、TUN 模型原理

UCloud技术

负载均衡

NumPy之:使用genfromtxt导入数据

程序那些事

Python 数据分析 Numpy 程序那些事

数仓ETL系统:给强大的“心脏”配上“超级流水线”

华为云开发者联盟

数据库 数据仓库 GaussDB(DWS) ETL系统 MPPDB

架构实战营 -- 模块三

永佳

架构实战营

Nginx 常用配置清单

Java小咖秀

nginx Web 反向代理 HTTP

第一个鸿蒙应用

释缘

鸿蒙 HarmonyOS

架构实战营模块3作业

Vic

架构实战营

rocketmq优雅停机往事

捉虫大师

PHP文件包含小总结

Thrash

安全

从5大挑战带你了解多模态机器学习

华为云开发者联盟

机器学习 多模态机器学习 多模态 异构数据

架构实战营模块三作业

竹林七贤

新书见面 | 《云原生时代的微服务架构实践》

Damon

微服务 云原生 5月日更

存算解耦的多模型数据管理平台介绍:以星环科技TDH8.0为例

星环科技

人工智能 大数据 云平台 数据管理平台 存算解耦

双向循环链表:鸿蒙轻内核中数据的“驿站”

华为云开发者联盟

鸿蒙 数据结构 结构体 OpenHarmony 双向循环链表

惊呆了!阿里自爆2021年度九大Java技术合集,Github上已获赞98.3K

Java架构师迁哥

看了小姐姐的Spring SPI 总结,双非渣硕的我差点跳起来,被征服了

牛哄哄的java大师

Java

如何做好用户画像?

石云升

创业 用户模型 5月日更

求求你别再用 MySQL offset 和 limit 分页了

xcbeyond

MySQL 5月日更

技术探索系列 - 轻松带你掌握JMM(2)

洛神灬殇

JVM JMM 5月日更

“区块链+疫情预警”!这个科研团队研发了传染病预警系统

CECBC

疫情

轶事

言未卜

闲鱼如何高效打造一个多业务、低侵入的搜索链路_架构_闲鱼技术_InfoQ精选文章