搜索引擎如何工作?

信息检索已经发展的非常成熟了,应该所有人都不陌生。我有幸这几年接触过并且实际做过一些搜索引擎开发的工作,特此总结并分享给大家。实际上,一个成熟的搜索引擎是想当复杂的,比如百度的,就分nginx,vui,us,as,bs,da.....等等这些模块,当然这些简写的字母大家也不必了解,只要知道它确实复杂就可以。

今天我所讲的是一个简化版的搜索引擎,简化到只涉及到倒排建立和拉取。虽然简单,但是它是整个搜索引擎的最核心组件。一个最简单的搜索引擎如下图所示:

  • merger: 接收查询请求,分词后请求下游Indexer分别获取各个indexer的局部TopK文档,归拢后排序返回全局相似度最高的TopK文档。

  • indexer:负责倒排拉取,并利用夹角余弦算法计算相似度,返回TopK结果。夹角余弦可以在 http://www.cnblogs.com/haolujun/p/8011776.html 中了解。

  • index:倒排索引。

倒排索引长成什么样子呢?就是图中标记的那样,每个词后面有一个拉链,拉链中存放包含该词的文档编号,利用这个数据结构能快速的找到包含某一个词的所有文档。

今天介绍则是搜索引擎的核心中的核心:倒排索引。接下来的所有内容都围绕着倒排索引展开。

如何建立倒排索引

对几亿甚至几十亿几百亿规模的文档集合建立倒排索引并不是一件很轻松的事情,它将面对着IO以及CPU计算的双重瓶颈,需要根据实际情况找到最优方法,接下来介绍两种不同的建立倒排的方式。

单遍内存型

内存中维护每个词对应的文档编号列表,当一个词对应的buffer满时,把内存中的数据flush到磁盘上,这样每个词对应一个文件。最后按照词编号由小到大的合并所有文件,得到最终的倒排索引。

  • 优点:使用内存存放倒排索引,并分批flush到磁盘。这样减少了一些排序操作,速度快。

  • 缺点:内存使用量稍大。可以减少内存buffer大小来降低内存使用量,但是这样做得缺点就是增加了磁盘随机写的次数。想想,每当一个buffer满时都会写对应的文件,并且buffer是随机的,所以增加了磁盘寻址的开销。

多路并归型

步骤如下:

  • 首先,解析文档,把写入到磁盘文件。

  • 然后,对磁盘文件进行外部排序,排序规则:按照词的字典序从小到大排序,如果词相同,则按照文档编号从小到大排序,这样相同的词就排到了一起。

  • 最后,顺序扫描排序后的文件,建立倒排索引。由于相同的词紧挨在一起,可以一个词一个词的建立倒排索引。

与内存型相比,这种方式适合在内存小,磁盘大的情况下进行倒排索引的建立,它的优缺点如下。

  • 优点:内存使用量小,没有磁盘随机读写,基本全是顺序读写。对于超大规模文档集合来说,这种方式相对灵活一些(主要是排序灵活)。

  • 缺点:多次归并排序,比较慢。但是,可以使用并发进行归并排序,这样能够提高一些速度。

索引切分

考虑到在海量文档下,倒排索引非常大,单台机器无法在内存中装下全部索引,所以有必要把索引进行切分,使得每一个索引服务只对文档中一部分的内容进行拉取、计算。常见的有两种可选择的方式。

按文档编号切

按照文档编号把文档分成几个小的集合,对每个小的文档集合单独建立索引。在这种方式建立索引上进行查询时,merge需要把查询请求下发到所有的后端indexer服务(因为每个index都有可能存在包含查询词的文档),indexer服务的计算量比较大。但是它也有一个优点:每个词的倒排拉链的长度可控。

按term切分

按照词进行索引划分,每个索引只保存若干词的所有文档编号。在这种方式建立的索引上进行查询时,merge可以根据查询词精确的把请求下发到对应的indexer上,减少了后端indexer的计算量。
但是这么做引入几个新问题:

  • 1:某些高频词倒排拉链过长,导致这台indexer计算时间超出可忍受范围,出现拖后腿现象;

  • 2:维护词表与indexer的对应关系,运维复杂;

  • 3:对于热词所在的索引,对应的indexer请求量大,计算量大,负载高,需要考虑热词打散。

那么在实际中该如何进行索引切分呢?主要看是什么类型的查询、查询的量、以及文档集合的规模。

  • 对于普通搜索引擎,用户输入的是数量较少的查询词,按照term切分可以有效减少查询时后端indexer的计算量,收益比较大。

  • 对于现在流行的拍照搜题,拍摄的图片中文字一般较多,按照term切分的优势不明显而且引入了复杂的运维,建议按照文档编号切分。

  • 对于高频词出现的文档,可以把这些文档选出来,然后按文档切分的方式建立索引,这样不会出现倒排拉链过长。总结下来就是:高频词按文档切分,低频词按照term切分,检索的时候,根据查询词是否是高频词执行不同的检索策略。

  • 在不差钱并且文档规模不大,并且查询量没有达到必须优化索引的前提下,尽量使用运维更简单的索引:按照文档编号切分。

增量索引

很多搜索引擎都注重时效性搜索,比如对于时下刚刚发生的某件热门事件,需要搜索引擎能够第一时间搜索到该热门事件的页面,这该如何做到呢?由于建立一次全量文档倒排的时间基本都是按天计,如果不设计一些实时增量索引,那么根本满足不了时效性的检索。下面介绍增量索引,可以解决时效性搜索问题。

倒排索引双buffer设计方案

增量步骤:

  • 1:indexer从索引2切换到索引1

  • 2:更新索引2

  • 3:indexer从索引1切回到索引2

  • 4:更新索引1

这样可以保证实时的动态更新,但是它的缺点也很明显:必须使用2倍索引大小的内存,机器成本比较高,实际中更常用的是下面一种方案。

增量索引服务+双buffer方案

全量索引服务用来查询截止到某一个日期的全部文档,增量索引服务使用双buffer设计方案查询最近一段时间(可能是小时级或者分钟级)内实时更新的文档内容,然后定期(每天、每周、每月一次)把最近一段时间更新的文档追加在全量索引中。这样做的好处就在于只有少量近期更新文档的查询需要使用双倍内存,机器成本降低。需要注意的一点是,用这种方式建立增量索引时,必须更新全局word的df信息,对于发现的新词还需为其添加全局唯一id,这些信息统统要更新到线上正在运行的全量索引服务。

利用Hadoop并行建立倒排索引

对于超大规模的文档集合,可以使用Hadoop建立倒排索引。

  • map端读入每个文档并进行解析,生成一个tuple,key为词,value为tf+文档编号,发送该tuple给下游reduce,这样相同的词会分配给同一个reduce。

  • reduce保存每个tuple,并在结束时对每个词倒排按照文档编号由小到大排序,并保存到对应的文件中。

  • 有多少个reduce就会有多少个索引文件,最后汇总这些文件得到最终的倒排索引。

实际工作中,为了增加map端读数据性能,并不是每个文档存放成单独一个文件,而是先把文档序列化成文件中的一行,这样每个文件可以存放多个文档内容,这就减少了小文件数,增加了map端的吞吐量。

总结

倒排的建立还有查询涉及到的技术内容远远不止于此,在这里可以推荐两本书给大家,有兴趣的小伙伴可以进行深入的学习,共同进步。

-《深入搜索引擎-海量信息的压缩、索引和查询》 Lan H.Witten, Alistair Moffat, Timothy C.Bell著,梁斌译。

-《信息检索导论》 Christopher D.Manning, Prabhakar Raghavan, Hinrich Schutze著,王斌译。

lucene 增量 全量 更新索引_搜索引擎:该如何设计你的倒排索引?相关推荐

  1. lucene 增量 全量 更新索引_10年+,阿里沉淀出怎样的搜索引擎?

    阿里妹导读:搜索引擎是阿里的10年+沉淀,具有很高的技术/业务/商业价值.1688很多场景都借助了搜索中台的能力,基于此,以1688主搜为例介绍搜索全链路知识点,希望对你有所借鉴,有所启发. 一.整体 ...

  2. 增量索引和全量索引_搜索引擎(七)高可用的solr搜索引擎服务架构

    经过前面一段时间的努力,终于把我所知道的关于solr 的内容都总结完了.前面讲到了solr 的安装配置,web管理后台的使用,solr 的查询参数和查询语法,还说到了solr的客户端 solrnet ...

  3. java 增量编译_java增量/全量编译接口应用

    JavaCompile Java compile是一个自动增量/全量编译java的api接口应用. 包含以下特性: 全量编译 增量编译 svnkit接口(svn操作web接口) 全流程使用案例 Jav ...

  4. hive增量表和全量表_基于 Flink + Hive 构建流批一体准实时数仓

    基于 Hive 的离线数仓往往是企业大数据生产系统中不可缺少的一环.Hive 数仓有很高的成熟度和稳定性,但由于它是离线的,延时很大.在一些对延时要求比较高的场景,需要另外搭建基于 Flink 的实时 ...

  5. mysqldump全量恢复_【MySQL】全量+增量的备份/恢复

    生产环境中,有时需要做MySQL的备份和恢复工作.因MySQL是在运行过程中的,做全量备份需要时间,全量备份完成后又有数据变动,此时需要增量备份辅助.如果想恢复数据到一个空库(例如数据迁移或者上云等更 ...

  6. hive增量表和全量表_你需要了解的全量表,增量表及拉链表

    mysql测试数据准备第一天 9月10号数据 1 2 31,待支付,2020-09-10 12:20:11,2020-09-10 12:20:11 2,待支付,2020-09-10 14:20:11, ...

  7. hive增量表和全量表_你真的了解全量表,增量表及拉链表吗?

    1 Mysql数据准备 第一天 9月10号数据 1,待支付,2020-09-10 12:20:11,2020-09-10 12:20:112,待支付,2020-09-10 14:20:11,2020- ...

  8. mysqldump全量恢复_删库不跑路-详解MySQL数据恢复

    日常工作中,总会有因手抖.写错条件.写错表名.错连生产库造成的误删库表和数据的事情发生,那么,如果连数据都恢复不了,还要什么 DBA. 相关文章 MySQL备份策略:https://segmentfa ...

  9. nwjs桌面应用升级方案 桌面应用更新 支持增量全量更新

    桌面应用程序的更新逻辑一般为:启动–检测版本号–有更新–下载新版本资源包–解压–替换文件–重启: 1.为了更加直观,请看5毛钱特效: 2.本文案例主要用nwjs,其他框架升级原理同理.updater. ...

最新文章

  1. XCODE 6.1.1 配置GLFW
  2. Spring Boot 项目瘦身指南,瘦到不可思议!
  3. 2021年春季学期-信号与系统-第十次作业参考答案-第二小题
  4. STM32:Flash擦除与读写操作(HAL库)
  5. 1Python生成高质量Html文件:Pyh模块+Bootstrap框架
  6. web程序前后台功能实现_好程序员web前端教程之JS继承实现方式解析
  7. 在Sping Boot logback的使用
  8. 获取date等于某一天的第一个id sql_leetcode(sql)之经典困难题合集
  9. java struts2下载文件_java struts2入门学习---文件下载的二种方式
  10. 【Android】Mac安装EasyTether导致无法识别设备的问题
  11. Java重构面向过程代码_代码重构那些事儿
  12. php安装调式redis扩展,下载安装thinkphp5.0,调试Redis是否可以正常使用
  13. 使用Excel功能抓取网页表格数据
  14. 浙江5G+智能制造迅速推进连点成片。
  15. 玩转Linux的下Ip计算器(图文)
  16. 常见路由器开源系统(固件)简介
  17. Java 中 字符串 1234 怎么转成 int?
  18. 使用wget下载GEO数据
  19. JMeter参数化post请求
  20. SSH Error: Permission denied (publickey)

热门文章

  1. netmiko 记录日志_Pythonnetmiko模块的使用 | 学步园
  2. 简单选择排序_Python3三种简单排序(冒泡、插入、选择)的比较
  3. 计算机类有电子商务类,电子商务类专业有哪些-电子商务类专业名单汇总
  4. python list add_Python的List方法附加和扩展有什么区别?
  5. 苹果4s怎么越狱_只有老外有空去玩这些,在苹果手机上刷入了安卓系统
  6. 周末ROS学习沙龙第一期——ROS历史、安装、消息话题节点服务等概念、SLAM导航框架及参数、小车上运行SLAM
  7. php获取系统常量函数,PHP:从php文件中获取PHP的变量,函数和常量
  8. 为什么先交钱后用电_车主快看,有上坡辅助,先抬离合后加油为什么不是熄火就是加空油...
  9. c索引超出了数组界限_关于MATLAB逻辑数组索引的二三事(二)
  10. java 面向对账 抽象_java开发银行支付、对账时证书相关的操作实例