背景说明

苏宁易购商品评价系统主要提供商品维度评价数量聚合、评价列表展示功能,并为其他业务系统提供商品评价数据支撑服务。功能涉及对亿级数据的数量聚合、排序、多维度查询等复杂的业务场景,关系型数据库的索引为B-Tree结构,适合数值区分度或离散度高的数据,而评价系统中单个商品评价可以达到数十万条,相同星级的评价数则为亿级,故不适宜使用关系型数据库。解决此类海量数据准实时聚合的技术选型有以倒排表作为索引结构的Solr和Elasticsearch(底层都是Apache Lucene)搜索引擎服务,还有以bitmap作为底层索引结构的实时分析统计数据库Druid,但Druid只是支持数据统计功能并不保存原始数据,无法满足商品评价类的功能需求。系统建设之初对团队对Solr更为熟悉,故在Solr和Elasticsearch两者之间选择的Solr作为商品评价索引数据存储服务。

评价系统架构

苏宁易购商品评价系统架构如下:


图2-1

应用服务模块

根据苏宁易购技术规范要求,应用系统架构划分为前中后台三个主模块:

  • 前台:为web、app提供页面或接口服务,接入苏宁统一认证系统Passport;
  • 中台:通过苏宁自研RSF远程服务框架对前台服务和其他业务系统提供功能接口服务;
  • 后台:业务系统运营管理功能和任务批处理。

数据存储层

  • MySQL:存储和查询登录用户发表的评价,具备ACID特性,按照用户ID分库分表,根据业务要求和存储容量限制只保留指定时间内的评价数据
  • Redis集群:苏宁在开源Redis中间件基础上自研的支持分布式集群、熔断、横向扩容的高可用、海量缓存服务架构,缓存商品评价数量和列表数据,承载用户端的数据访问请求,降低对业务系统的访问压力
  • HBase:海量评价内容存储
  • Solr:评价索引存储和查询服务

外围系统

  • 商品中心:通过MQ(异步)和RSF(同步)接收保存商品主数据如商品信息、类目信息、店铺信息、商品关系信息等
  • 订单中心:待评商品主要来源于订单中心通过MQ下发的订单信息和订单状态通知,并由评价系统提供订单待评状态查询RSF接口,在订单页面展示商品评价发表入口
  • 搜索产品线:通过MQ下发商品评价数量到搜索产品线,为商品搜索排序计算提供数据支持

基础设施

  • 分布式任务调度系统(UTS):对各业务系统中的定时任务进行集中管理和调度,解决了集群环境下任务并发执行的控制,让业务系统从繁琐的技术细节中释放出来,并提供了对任务调度执行情况的监控和异常短信告警、重试机制,且提供跨业务系统的后继任务调度。
  • 集中配置管理(SCM):基于Zookeeper自研的应用配置信息集中统一管理和实时推送服务框架。
  • 消息中间件:由于历史原因,苏宁内部有多套MQ中间件,包括IBM MQ,Kafka和自研的WindQ。IBM MQ已经不建议使用,正在逐渐废弃;Kafka在大数据场景下使用较多;业务系统推荐使用WindQ,解决了解决多活场景下消息路由,提供消息发送重试熔断、在线动态扩缩容、顺序消费、异构MQ桥接等特性。

监控

  • 日志:改造Solr日志模块,接入苏宁统一日志框架,便于异常问题分析
  • 主机:接入Zabbix监控体系,监控主机和应用健康状态
  • 系统:接入苏宁云迹系统,监控Solr请求响应TP90/99等指标

Solr介绍

搜索引擎基础原理

搜索引擎的索引称为反向索引,俗称倒排表,把文本分词得到字典,保存字典项与文档ID(Lucene自身doc ID而非应用端文档ID)的关系,在查询时根据字典查询到倒排表文档ID集合,再进行交并集操作即可得到结果,其主要结构是字典域、索引域和字段存储域。反向索引如下图:


图3-1

在Solr4.0之后为了满足排序和关键字聚合的需求场景,Lucene提供了DocValues特性,又被称为正排表,使用列式存储保存文档ID和字典项的关系,不再使用之前FieldValue Cache机制,提升性能并降低对内存的使用和虚拟机Full GC的风险。在Elasticsearch中所有字段都是默认开启此特性,但在Solr中需要使用者进行显式配置生效。DocValues存储结构可分为两种,一种是原值,一种是字典项ID。

  • 单数值型和原始字节型字段,其基本结构包含字段原值的long[]数组,如带有三个数值的文档DocValues结构:
doc[0] = 1005doc[1] = 1006doc[2] = 1005
  • 其他可索引类型字段,其结构也是一个long[]数组,只不过数组中的值为字典项ID(可能有多个),例如有3个字符型字段的文档:
doc[0] = \u0026quot;aardvark\u0026quot;doc[1] = \u0026quot;beaver\u0026quot;doc[2] = \u0026quot;aardvark\u0026quot;

假如“aardvark”在字典表中ID为0,”beaver”在字典中ID为1,则实际结构为:

doc[0] = 0doc[1] = 1doc[2] = 0

字典表数据为:

term[0] = \u0026quot;aardvark\u0026quot;term[1] = \u0026quot;beaver\u0026quot;

注:字典表所有term都是有序的,故DocValues可以直接用于排序。

Solr\u0026amp;Lucene架构体系

Solr是一个高性能、基于Lucene的开源全文搜索服务,提供丰富的查询语言,同时实现了可配置、可扩展并对查询性能提供了优化,提供完善的功能管理界面;在Lucene基础上对易用性和可用性进行了大量封装如Schema配置化、请求分发处理机制、插件化机制、数据导入、分布式、监控指标采集等,架构体系如下:


图3-2

苏宁商品评价系统结合自身业务特点采用了Solr主从节点和聚合查询节点的组合架构,而不是通用的Solr Cloud架构,主要考虑到两点:

  • 横向扩容机制完全可控,根据商品编码单调递增特性,可以随时扩充新节点,节点路由机制由业务系统控制。
  • 单独搭建聚合查询节点,在跨多个Solr节点查询时由聚合节点实现聚合查询功能,特别是在聚合商品评价列表查询场景下,降低对数据节点的性能压力。

Solr特性应用

多维度数量聚合

商品详情页展示商品/供应商维度审核通过的好中差评数量、标签数量、个性化评价项等数量,使用的是Solr facet机制。


图4-1

  • 单个字段facet如好中差评数据量(上图方框处)涉及星级字段直接设置facet.field参数
/solr /select?q=*:*\u0026amp;wt=json\u0026amp;indent=true\u0026amp;facet=true\u0026amp;facet.field=qualityStar
  • 多条件facet如有图评价和已追评(上图圆圈处)评价数量,应使用facet.query机制把每个facet.qery查询当做单独facet值,一次性查询出结果而不是发起多次普通总数查询
/solr/select?q=*:*\u0026amp;wt=json\u0026amp;indent=true\u0026amp;facet=true\u0026amp;facet.query=picVideoFlag:1\u0026amp;facet.query=againReviewFlag:1

多维度列表查询

根据查询条件直接查询评价ID即可,限制查询字段并支持分页,因商品评价无需计算文档相似度且开启缓存可提升查询性能,故使用filter query替代query作为查询条件:

/solr/select?q=*:*\u0026amp;fq=(auditStat:0+OR+auditStat:1)\u0026amp;start=0\u0026amp;rows=10\u0026amp;fl=commodityReviewId

需要注意的是filter cache是以单个filter query为键缓存结果,可以根据业务需求要求拆成多个filter query,设置不同缓存策略以提升缓存利用率。

分组查询

需要说明的是Solr分组关键字group的含义与关系型数据库的group by中的group并不相同,在Solr中指的是对查询的结果文档根据指定的字段进行分组,而非关系型数据库对字段分组,如苏宁商品评价需要展示每个评价的第一条商家回复内容,通过Solr查询商家回复并根据评价ID进行分组后取第一条记录即可(查询条件为评价ID列表),参数说明如下:


表4-1

自定义排序

在使用Solr对结果集排序一般有两种方式:

  • 一是在写入索引时单独字段保存数值,在查询时使用sort字段直接排序即可,优点是简单,但调整排序规则时需要重建所有索引。

  • 二是函数查询机制,在Solr标准查询解析器中使用solr内置的函数,指定sort字段为函数内容或在DisMax中指定bf参数都可以满足业务需求,例如sort=div(popularity,price)desc,score desc格式,div函数表示popularity和price两个字段相除。

大部分排序都使用第二种方式,但此方式对性能会有影响,特别是在涉及到多个字段时且参与排序的文档数较多时,其内部执行过程需要获取每个匹配doc的字段值进行计算,即使字段开启docValues特性对存储、IO和内存空间也有一定压力。

提升函数排序的性能也有两种方式:

  • 一是粗略的计算排序值,或写索引时即有一定顺序,查询时进行截断,只返回一定数量的文档,再进行二次排序,即通常电商搜索中粗排和精排过程。
  • 二是把涉及到的排序字段合并到一个字段并开启docValues特性,例如【评价时间|会员等级|评价星级|内容长度|图片个数|内容质量分|图片质量分|人工审核权重】此种格式,并编写自定义函数解析计算这个组合字段的排序值;继承org.apache.Lucene.queries.function.ValueSource实现排序值计算,继承org.apache.solr.search.ValueSourceParser实现此函数的解析创建即可。这也是苏宁商品评价系统准备采用的方式。

facet.method参数的选择

Lucene字段级facet有三种机制:

  • enum:遍历字段下所有的字典项,对所有字典项的倒排文档ID和查询结果文档ID进行交集运算得到结果
  • fc:根据查询结果的每个文档ID从Field Cache中查询字段值,计算每个字段值的次数得到结果
  • fcs:类似于fc,不同点是基于Lucene中每个段单独的Field Cache

facet.method参数指定在对字段进行聚合时使用上述哪种算法,根据上述实现机制说明可知对于值区分度低的字段,适合选择enum机制;对于有大量不同值的字段合适使用fc机制,若Solr开启了近实时搜索(NRT)特性,fcs机制则是更好的选择,因其在生成新索引段时旧索引段缓存不用重新加载。

分布式聚合查询

苏宁易购商品电商模型中SPU和SKU可以在商品上架时根据产品特性和销售情况动态调整组合,查询SPU商品评价时就会出现跨多个节点的场景,需要支持跨节点的分布式查询并对聚合结果集进行二次处理,如数量的累加、列表二次排序和分页等,为此搭建空数据的Solr节点作为聚合查询节点。业务方根据SPU和SKU关系得出节点编号和节点的地址,改写查询请求添加shards参数转发给聚合节点即可,示例如下:

/solr/select?q=*:*\u0026amp;wt=json\u0026amp;indent=true\u0026amp;facet=true\u0026amp;facet.query=picVideoFlag:1\u0026amp;facet.query=againReviewFlag:1\u0026amp;shards=solr1:8080/,solr2:8080/,solr3:8080/

此特性原理是使用多线程查询多个Solr节点,在内存中对结果进行合并,故使用此特性也有一定限制:

  • 列表分页须限制大页码,防止列表合并分页时内存溢出或产生Full GC问题,性能急剧下降并可能拖垮服务节点
  • 相关性排序和facet特性可能与单节点查询结果可能不同
  • 注意设置线程池数量和HTTP超时时长
  • 文档在所有节点必须唯一,不允许重复
  • 不支持pivot facet和join特性

性能调优

  • 开启filter cache,以查询条件为key,文档ID列表为值保存在cache中,可以大幅提升数量聚合性能,但需要注意不适合字段值离散度较高的查询,否则产生大量key会把cache中的热点缓存替换出去,缓存命中率下降反而影响性能,可以通过设置cache=false或在函数查询中设置fq={! cache=false}参数屏蔽当前请求的cache机制

  • 文档数较多且命中率低,需要关闭documentCache和queryResultCache

  • 设置newSearcher和firstSearcher两个Listener的查询语句,对重新打开的IndexSearcher进行预热

  • JVM使用CMS GC策略,并设置CMSInitiatingOccupancyFraction值为60,保证在主从同步时Solr有足够的堆内存,降低因CMS GC内存碎片导致Full GC的风险。

配置示例如下:

\u0026lt;query\u0026gt;\t\u0026lt;maxBooleanClauses\u0026gt;2000\u0026lt;/maxBooleanClauses\u0026gt;\t\u0026lt;filterCache class=\u0026quot;solr.FastLRUCache\u0026quot; size=\u0026quot;8192\u0026quot; initialSize=\u0026quot;4096\u0026quot; autowarmCount=\u0026quot;80%\u0026quot; /\u0026gt;\t\u0026lt;enableLazyFieldLoading\u0026gt;true\u0026lt;/enableLazyFieldLoading\u0026gt;\t\u0026lt;listener event=\u0026quot;newSearcher\u0026quot; class=\u0026quot;solr.QuerySenderListener\u0026quot;\u0026gt;\t\t\u0026lt;arr name=\u0026quot;queries\u0026quot;\u0026gt;\t\t\t\u0026lt;!--预加载查询--\u0026gt;\t\t\u0026lt;/arr\u0026gt;\t\t\u0026lt;/listener\u0026gt;\t\u0026lt;listener event=\u0026quot;firstSearcher\u0026quot; class=\u0026quot;solr.QuerySenderListener\u0026quot;\u0026gt;\t\t\u0026lt;arr name=\u0026quot;queries\u0026quot;\u0026gt;\t\t\t\u0026lt;!--预加载查询--\u0026gt;\t\t\u0026lt;/arr\u0026gt;\t\u0026lt;/listener\u0026gt;\t\u0026lt;useColdSearcher\u0026gt;false\u0026lt;/useColdSearcher\u0026gt;\t\u0026lt;maxWarmingSearchers\u0026gt;1\u0026lt;/maxWarmingSearchers\u0026gt;\u0026lt;/query\u0026gt;

展望

目前在CDN缓存和Redis缓存失效情况部分请求的压力还是回源到Solr,对于Solr的直接依赖较大,且更新索引时偶尔还是会触发Full GC,影响业务的稳定性。下一步需要对缓存架构重新设计,设计缓存异步更新和熔断机制,限制用户请求直接落到Solr服务上,提升接口QPS、系统稳定性和评价展示生效实时性。

另外还在规划引入Elasticsearch作为后台业务索引,前后台索引进行分离,降低前后台耦合度和系统风险,更便于业务开发。

作者介绍

胡正林,苏宁易购IT总部消费者平台研发中心高级架构师,十余年软件开发经验,熟悉大型分布式高并发系统架构和开发,目前主要负责易购各系统架构优化与大促保障工作。

苏宁11.11:搜索引擎Solr在苏宁易购商品评价系统中的应用相关推荐

  1. 苏宁易购亿万级商品评价系统的架构演进之路和实现细节

    苏宁易购评价系统跟随着易购商城的业务发展,经历了从Commerce系统拆分再到系统全面重构的整个历程.如何满足系统流量的日益增长,在提升系统性能和满足稳定性和可扩展性的要求的同时,向目标系统架构一步步 ...

  2. 爬虫项目十:Python苏宁易购商品数据、评论数据爬取

    文章目录 前言 一.商品数据 1.分析url 2.解析数据 3.实现翻页 二.评论数据 前言 利用Python对苏宁易购商品数据评价数据实现爬取 提示:以下是本篇文章正文内容,下面案例可供参考 一.商 ...

  3. 苏宁11.11:苏宁易购订单搜索系统架构及实现

    背景 随着苏宁易购平台规模的飞速发展,平台的订单量呈现指数级的增长,存储容量已达TB级,订单量更是到了万亿级别,尤其在双11大促流量洪峰的场景下,面临两个挑战: 1.如何存储如此巨大的数据量 2.如何 ...

  4. 苏宁易购商品详情API接口

    API 名称: item_get - 获得suning商品详情 num_iid=0070134261/703410301 (num_iid:店铺ID/商品ID) 欢迎使用亚苏宁易购API接口 API测 ...

  5. python 爬取苏宁易购商品信息和评论的详细流程

    总体说明 爬取苏宁易购的最大难点就在于他这个页面是很多js动态加载的内容,寻找和构造链接比较麻烦,如果不用JS逆向工程.采用selenium等爬取的效率相对会比较差一点,下面我会先放上我找的js动态加 ...

  6. 基于python苏宁易购商品信息爬取

    本文思路来源崔庆才老师的淘宝商品爬取 -首先打开苏宁易购网站,找到搜索框以及搜索按钮接口,模拟人工操作,输入关键词,并进行点击搜索操作,进入到商品的详细页. from selenium import ...

  7. 获取苏宁易购商品的评论图片

    苏宁易购评论图怎么保存?可以在电脑上操作吗?下图高手可以支持吗?小编的回答当然是可以的,下面一起来试试. 进入苏宁易购,输入关键词,挑选商品 喜欢哪个商品进入页面,选中并复制上方的链接 把复制好的链接 ...

  8. 如何一键批量采集苏宁易购商品主图、详情图、及视频

    一.所用采集工具(载图助手)百度上可以查找并下载安装. 1.在浏览器打开苏宁易购平台,找到您所要采集的商品类目,然后点击一个商品进入,如图: 2.启用软件的批量下载功能打开"自动粘贴网址&q ...

  9. 苏宁易购商品详情 API

    onebound.suning.item_get 注册使用接口 api文档 响应示例 "item": {         "num_iid": "00 ...

  10. 获取苏宁易购商品信息操作详情

    一.注册 在使用API之前,首先您需要注册一个属于自己的key,PI网站注册后最重要的个人数据是请求key值,每个不同的数据都有与之对应的请求key值,因此账号一定要妥善保存 点击注册 二.使用 展示 ...

最新文章

  1. linux下抓包代码阿里云,10.10 linux下抓包
  2. Android Studio 怎样打JAR包
  3. Tensorflow学习
  4. bool,_Bool和BOOL的区别
  5. 看下你是否都做了吗?系统140种实用设置2
  6. 网页登陆注册(jsp实现)验证码
  7. 电动汽车真的省钱吗?
  8. 实战篇:如何建设企业的营销管理和分析平台
  9. 第五章 循环结构课后思考
  10. oracle和sqlserver数据库直接生成xml
  11. matlab时空地理回归,★时空地理加权回归space-time GWR GTWR分析软件-香港中文大学-黄波教授...
  12. 网络模型早停earlystopping详解
  13. 2.2基本共射放大电路的工作原理
  14. DEL: 华为无线modem变无线路由器 2
  15. CF 613C(Necklace-构造法)
  16. 复合材料在计算机硬件中的应用,碳纤维复合材料在笔记本电脑后盖中的应用研究...
  17. 行业资讯 | 深圳:BIM法定化,开历史之先河
  18. 第五章 全连接神经网络
  19. Jenkins之自动进行360加固
  20. 批处理和流处理如何区分? 有边界数据和无边界数据如何区分?

热门文章

  1. android 脚本swipe,appium1.1 版本使用 swipe 方法报错
  2. 数字逻辑电路 逻辑运算 与、或、非、与非、或非、与或非、异或、同或 二进制运算技巧
  3. 使用google的zxing制作二维码
  4. iphone 蓝牙开发 总结
  5. 实践篇(4):Apache jena SPARQL endpoint及推理
  6. IO中write函数
  7. 从 0 开始学游戏开发
  8. 今天是个好日子 12月17日
  9. 2022CTFSHOW菜狗杯部分题目(四)
  10. 【题解】【PTA里的Python题库】7-1 身份证校验_python