【搜索引擎设计:信息搜索怎么避免大海捞针?

在前面我们提到了网页爬虫设计:如何下载千亿级网页?中,我们讨论了大型分布式网络爬虫的架构设计,但是网络爬虫只是从互联网获取信息,海量的互联网信息如何呈现给用户,还需要使用搜索引擎完成。因此,我们准备开发一个针对全网内容的搜索引擎,产品名称为“Bingoo”。

Bingoo 的主要技术挑战包括:

  1. 针对爬虫获取的海量数据,如何高效地进行数据管理;
  2. 当用户输入搜索词的时候,如何快速查找包含搜索词的网页内容;
  3. 如何对搜索结果的网页内容进行排序,使排在搜索结果列表前面的网页,正好是用户期望看到的内容。

因此,针对此类问题,我们开发一个搜索引擎系统!

1、概要设计

一个完整的搜索引擎包括分布式爬虫、索引构造器、网页排名算法、搜索器等组成部分,Bingoo 的系统架构如下:

image-20231226130403138

分布式爬虫通过存储服务器将爬取的网页存储到分布式文件集群 HDFS,为了提高存储效率,网页将被压缩后存储。存储的时候,网页一个文件挨着一个文件地连续存储,存储格式如下:

image-20231226130455447

每个网页被分配得到一个 8 字节长整型 docID,docID 之后用 2 个字节记录网页的 URL的长度,之后 4 个字节记录压缩后网页内容数据的长度,所有存储的网页的头 14 个字节都是同样的格式。之后存储 URL 字符串和压缩后的网页内容数据。读取文件的时候,先读14 个字节的头信息,根据头信息中记录的 URL 长度和数据长度,再读取对应长度的 URL和网页内容数据。

搜索引擎能够快速查找的核心就是利用索引,根据用户的查询内容查找匹配的索引,根据索引列表构建结果页面。索引的构造主要通过索引构造器完成,索引构造器读取 HDFS 中的网页内容,解压缩后提取网页中的单词,构建一个“docID-> 单词列表”的正排索引。然后,索引构造器再根据这个正排索引构建一个“单词 ->docID 列表”的倒排索引,“docID 列表”就是包含了这个单词的所有网页列表。利用这个倒排索引,搜索器可以快速获得用户搜索词对应的所有网页。

网页中所有的单词构成了一个词典,实际上,词典就是一个 Hash 表,key 就是单词,value 就是倒排索引的网页列表。虽然互联网页的内容非常庞大,但是使用到的单词其实是非常有限的。根据 Google 的报告,256M 内存可以存放 1400 万个单词,这差不多就是英文单词的全部了。

在构建索引的过程中,因为要不断修改索引列表,还要进行排序,所以,有很多操作是需要进行加锁同步完成的。对于海量的互联网页的计算,这样的索引构建速度太慢了。因此我们设计了 64 个索引桶,根据 docID 取模,将不同网页分配到不同的桶中,在每个桶中分别进行索引构建,通过并行计算来加快索引处理速度。

索引构造器在读取网页内容、构造索引的时候,还会调用 URL 提取器,将网页中包含的URL 提取出来,构建一个链接关系表。链接关系表的格式是“docID->docID”,前一个docID 是当前网页的 docID,后一个 docID 是当前网页中包含的 URL 对应的 docID。一个网页中会包含很多个 URL,也就是会构建出很多个这样的链接关系。后面会利用这个链接关系表,使用 PageRank 排名算法对所有网页进行打分排名,当索引器得到查找的网页列表时,利用 PageRank 值进行排名,最终呈现给用户,保证用户最先看到的网页是最接近用户期望的结果页面。

2、详细设计

一个运行良好的搜索引擎的核心技术就是索引和排名,所以我们将分别说明这两种技术要点!

1、索引

索引构造器从 HDFS 读取网页内容后,解析每个页面,提取网页里的每个单词。如果是英文,那么每个单词都用空格分隔,比较容易;如果是中文,需要使用中文分词器才能提取到每个单词,比如“高并发架构”,使用中文分词器得到的就是“高并发”、“架构”两个词。

首先,索引构造器将所有的网页都读取完,构建出所有的“docID-> 单词列表”正排索引。

image-20231226131318766

然后遍历所有的正排索引,再按照“单词→docID 列表”的方式组织起来,就是倒排索引了。

image-20231226131337477

我们这个例子中只有两个单词、7 个网页。事实上,Bingoo 数以千亿的网页就是这样通过倒排索引组织起来的,网页数量虽然庞大,但是单词数却是比较有限的。所以,整个倒排索引的大小相比于网页数量要小得多。Bingoo 将每个单词对应的网页列表存储在硬盘中,而单词则存储在内存的 Hash 表,也就是词典中,词典示例:

image-20231226131444415

对于部分热门的单词,整个网页列表也可以存储在内存中,相当于缓存。在词典中,每个单词记录下硬盘或者内存中的网页列表地址,这样只要搜索单词,就可以快速得到对应的网页地址列表。Bingoo 根据列表中的网页编号 docID,展示对应的网页信息摘要,就完成了海量数据的快速检索。

如果用户的搜索词正好是一个单词,比如“高并发”,那么直接查找词典,得到网页列表就完成查找了。但是如果用户输入的是一个句话,那么搜索器就需要将这句话拆分成几个单词,然后分别查找倒排索引。这样的话,得到的就是几个网页列表,还需要对这几个网页列表求交集,才能得到最终的结果列表。

比如,用户输入“高并发架构”进行搜索,那么搜索器就会拆分成两个词:“高并发”、“架构”,得到两个倒排索引:

  • 高并发 ->2,3,5,7

  • 架构 ->1,2,4

需要对这两个倒排索引求交集,也就是同时包含“高并发”和“架构”的网页才是符合搜索要求的结果,最终的交集结果应该是只有一篇网页,即 docID 为 2 的满足要求。

列表求交集最简单的实现就是双层 for 循环,但是这种算法的时间复杂度是 O(n^2),我们的网页列表长度(n)可能有千万级甚至更高,这样的计算效率太低。

一个改进的算法是拉链法,我们将网页列表先按照 docID 的编号进行排序,得到的就是这样两个有序链表:

image-20231226131628561

同时遍历两个链表,如果其中一个链表当前指向的元素小于另一个链表当前指向的元素,那么这个链表就继续向前遍历;如果两个链表当前指向的元素相同,该元素就是交集元素,记录在结果列表中;依此继续向前遍历,直到其中一个链表指向自己的尾部 nil。

拉链法的时间复杂度是 O(2n),远优于双层循环。但是对于千万级的数据而言,还是太慢。我们还可以采用数据分片的方式进行并行计算,以实现性能优化。

比如,我们的 docID 分布在[0, 1 万亿) 区间,而每个倒排索引链表平均包含 1 千万个docID。我们把所有的 docID 按照 1 千亿进行数据分片,就会得到 10 个区间[0, 1 千亿)[1千亿,2 千亿)……[9 千亿,1 万亿)。每个倒排索引链表大致均匀分布在这 10 个区间,我们就可以依照这 10 个区间范围,将每个要遍历的链表切分为 10 片,每片大约包含 1 百万个 docID。两个链表只在自己对应的分片内求交集即可,因此我们可以启动 10 个线程对10 个分片进行并行计算,速度可提高 10 倍。

事实上,两个 1 千万长度的链表求交集,最终的结果可能不过几万,也就是说,大部分的比较都是不相等的。比如下面的例子。

image-20231226131736242

第一个链表遍历到自己的最后一个元素,才和第二个链表的第一个元素相同。那么第一个链表能不能跳过前面那些元素呢?很自然,我们想到可以用跳表来实现,如下图:

image-20231226131804802

跳表实际上是在链表上构建多级索引,在索引上遍历可以跳过底层的部分数据,我们可以利用这个特性实现链表的跳跃式比较,加快计算速度。使用跳表的交集计算时间复杂度大约是 O(log(n))。

此外,虽然搜索引擎利用倒排索引已经能很快得到搜索结果了,但搜索引擎应用还会使用缓存对搜索进行加速,将整个搜索词对应的搜索结果直接放入缓存,以减少倒排索引的访问压力,以及不必要的集合计算。

2、PageRank 排名算法

Bingoo 使用 PageRank 算法进行网页结果排名,以保证搜索结果更符合用户期待。

PageRank 算法会根据网页的链接关系给网页打分。如果一个网页 A 包含另一个网页 B 的超链接,那么就认为 A 网页给 B 网页投了一票。一个网页得到的投票越多,说明自己越重要;越重要的网页给自己投票,自己也越重要。

PageRank 算法就是计算每个网页的 PageRank 值,最终的搜索结果也是以网页的PageRank 值排序,展示给用户。事实证明,这种排名方法非常有效,PageRank 值更高的网页,确实更满足用户的搜索期望。

以下面四个网页 A、B、C、D 举例,带箭头的线条表示链接。

image-20231226132710017

B 网页包含了 A、D 两个页面的超链接,相当于 B 网页给 A、D 每个页面投了一票,如果初始的时候,所有页面都是 1 分,那么经过这次投票后,B 给了 A 和 D 每个页面 1/2 分(B 包含了 A、D 两个超链接,所以每个投票值 1/2 分),自己从 C 页面得到 1/3 分(C包含了 A、B、D 三个页面的超链接,每个投票值 1/3 分)。

而 A 页面则从 B、C、D 分别得到 1/2,1/3,1 分。用公式表示就是

image-20231226132751916

等号左边是经过一次投票后,A 页面的 PageRank 分值;等号右边每一项的分子是包含 A页面超链接的页面的 PageRank 分值,分母是该页面包含的超链接数目。

这样经过一次计算后,每个页面的 PageRank 分值就会重新分配,重复同样的算法过程,经过几次计算后,根据每个页面 PageRank 分值进行排序,就得到一个页面重要程度的排名表。根据这个排名表,将用户搜索出来的网页结果排序,排在前面的通常也正是用户期待的结果。

但是这个算法还有个问题,如果某个页面只包含指向自己的超链接,其他页面不断给它送分,而自己一分不出,随着计算执行次数越多,它的分值也就越高,这显然是不合理的。这种情况就像下图所示的,A 页面只包含指向自己的超链接。

image-20231226132818673

解决方案是,设想浏览一个页面的时候,有一定概率不是点击超链接,而是在地址栏输入一个 URL 访问其他页面,表示在公式上,就是

image-20231226132837504

上面 (1 - a)就是跳转到其他任何页面的概率,通常取经验值 0.15(即 为 0.85),因为有一定概率输入的 URL 是自己的,所以加上上面公式最后一项,其中分母 4 表示所有网页的总数。

那么对于 N 个网页,任何一个页面 的 PageRank 计算公式如下:

image-20231226132918850

公式中 Pj ∈ M(P**i), 表示所有包含有 超链接的 , 表示 页面包含的超链接数,N 表示所有的网页总和。由于 Bingoo 要对全世界的网页进行排名,所以这里的 N 是一个万亿级的数字。

计算开始的时候,将所有页面的 PageRank 值设为 1,带入上面公式计算,每个页面都得到一个新的 PageRank 值。再把这些新的 PageRank 值带入上面的公式,继续得到更新的PageRank 值,如此迭代计算,直到所有页面的 PageRank 值几乎不再有大的变化才停止

3、总结

PageRank 算法我们现在看起来平平无奇,但是正是这个算法造就了 Google 近 2 万亿美元的商业帝国。在 Google 之前,Yahoo 已经是互联网最大的搜索引擎公司。按照一般的商业规律,如果一个创新公司不能带来十倍的效率或者体验提升,就根本没有机会挑战现有的巨头。而 Google 刚一出现,就给 Yahoo 和旧有的搜索引擎世界带来摧枯拉朽的扫荡,用户体验的提升不止十倍,这其中的秘诀正是 PageRank。

二十几年前,我刚刚接触编程的时候,我们中国也有很多这样的编程英雄,王选、王江民、求伯君、雷军等等,他们几乎凭一己之力就创造出一个行业。正是对这些英雄们的崇拜和敬仰,引领我在编程这条路上一直走下去。软件编程是一个可以创造奇迹的地方,而不只是为了混碗饭吃。梦想不能当饭吃,但是梦想带来的可不止是一碗饭。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/317501.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机组成原理 CPU的功能和基本结构和指令执行过程

文章目录 CPU的功能和基本结构CPU的功能CPU的基本结构 指令执行过程指令周期概念指令执行方案指令数据流取周期数据流析指周期数据流执行周期数据流中断周期数据流 数据通路的功能和基本结构数据通路的功能数据通路的结构单总线 CPU的功能和基本结构 #mermaid-svg-jr0QOEyC6Q92…

【MySQL】MySQL表的约束-空属性/默认值/列属性/zerofill/主键/自增长/唯一键/外键

文章目录 表的约束1.空属性 --null && not null2.默认值 -- default3.列描述4.zerofill5.主键6.自增长7.唯一键8.外键 表的约束 表的约束:表中一定要有各种约束,通过约束,让我们未来插入数据库表中的数据是符合预期的。约束的本质是…

8年经验分享:想要成为一名合格的软件测试工程师,你得会些啥?

对于很多新入行或者打算入行,成为软件测试工程师的小伙伴来说,刚开始接触这行,不知道自己究竟该学些什么,或者不知道必须掌握哪些知识,才能成为一名合格的测试工程师。 根据笔者观点,如果你能在学习过程中&…

Springboot项目Nacos做配置中心

Springboot项目Nacos做配置中心 说明安装2.Springboot整合使用Nacos3.问题处理 说明 文档参考 Nacos Spring Boot 安装 查看nacos镜像 docker search nacos 下载镜像 docker pull nacos/nacos-server启动naocs镜像 docker run --env MODEstandalone --name nacos -d -p 8…

Android Studio导入项目 下载gradle很慢或连接超时

AS最常见的问题之一就是下载gradle非常慢,还经常出现下载失败的情况,没有gradle就无法build项目,所以一定要先解决gradle的下载问题,下面教大家两种常用方法。 因为我的项目绝大多数使用的是gradle-5.6.4-all,下面就以…

将Python 程序封装成exe程序(保姆级教程)

前言 常用的软件都是带有操作界面的(Graphical User Interface,GUI),其目的就是在用户不需要看懂程序 底层代码的同时也可以进行软件相关功能的操作,更便于用户与程序的交互。随着Python逐渐成为一种流行编程工具的同时…

从混沌到有序:2023年全球软件质量与效能大会中的运维经验分享

在当今这个信息化社会,软件已经成为了我们生活和工作中不可或缺的一部分。然而,随着软件应用的普及和复杂度的增加,如何保障软件的质量和效能已经成为了一个重要的问题。 2023年全球软件质量与效能大会(QECon上海站)汇…

前端基础知识整理汇总(上)

HTML页面的生命周期 HTML页面的生命周期有以下三个重要事件&#xff1a; DOMContentLoaded —— 浏览器已经完全加载了 HTML&#xff0c;DOM 树已经构建完毕&#xff0c;但是像是 <img> 和样式表等外部资源可能并没有下载完毕。 load —— 浏览器已经加载了所有的资源&…

CSS3动画效果详解

CSS3动画 在CSS3中&#xff0c;animation属性用于实现元素的动画。 animation属性跟transition属性在功能实现上是非常相似的&#xff0c;都是通过改变元素的属性值来实现动画效果。但是&#xff0c;这两者实际上有着本质的区别 对于transition属性来说&#xff0c;它只能将…

CMake在静态库中链接动态库

hehedalinux:~/Linux/multi-v3$ tree . ├── calc │ ├── add.cpp │ ├── CMakeLists.txt │ ├── div.cpp │ ├── mult.cpp │ └── sub.cpp ├── CMakeLists.txt ├── include │ ├── calc.h │ └── sort.h ├── sort │ ├── …

Android中集成FFmpeg及NDK基础知识

前言 在日常App开发中,难免有些功能是需要借助NDK来完成的,比如现在常见的音视频处理等,今天就以ffmpeg入手,来学习下Android NDK开发的套路. JNI和NDK 很多人并不清除JNI和NDK的概念,经常搞混这两样东西,先来看看它们各自的定义吧. JNI和NDK 很多人并不清除JNI和NDK的概念…

14-部署Kafkasource和KafkaChannel

部署KafkaSource KafkaSource负责将Kafka中的消息记录转为CloudEvents 仅在需要从Kafka中加载消息并提供给Knative Eventing上的应用程序使用时才需要KafkaSource 命令&#xff1a; kubectl apply -f https://github.com/knative-extensions/eventing-kafka-broker/releases/…

确保CentOS系统中的静态HTTP服务器的数据安全

确保CentOS系统中的静态HTTP服务器的数据安全是一项重要的任务&#xff0c;它有助于保护网站免受未经授权的访问、数据泄露和其他安全威胁。以下是一些关键步骤和最佳实践&#xff0c;以确保CentOS系统中静态HTTP服务器的数据安全&#xff1a; 限制访问权限确保只有授权用户可…

【教程】微信小程序如何拍摄图片及视频并上传到后台进行存储

需求分析 在微信小程序中需要使用手机拍摄照片以及视频上传到后台进行进一步的操作&#xff0c;需要解决以下两个问题&#xff1a; 微信小程序如何拍摄照片及视频如何将拍摄的照片及视频上传到后台进行存储 解决方案 前端开发&#xff1a;微信小程序原生 后端开发&#xf…

【分布式技术】监控平台zabbix自定义模板、设置邮件报警、导入模板

目录 案例&#xff1a;监控当前登录人数&#xff0c;超过3人触发报警发送邮件 第一步&#xff1a;自定义模板 1、明确想要获取监控数据的命令和脚本 ​编辑 2、在被监控主机上&#xff0c;修改zabbix agent2的配置文件或者在zabbix agent2的配置文件目录中添加以.conf结尾…

前端八股文(性能优化篇)

目录 1.CDN的概念 2.CDN的作用 3.CDN的原理 4.CDN的使用场景 5.懒加载的概念 6.懒加载的特点 7.懒加载的实现原理 8.懒加载与预加载的区别 9.回流与重绘的概念及触发条件 &#xff08;1&#xff09;回流 &#xff08;2&#xff09;重绘 10. 如何避免回流与重绘&#…

视频剪辑达人分享:批量减片时时长并调整播放倍速的技巧

在视频剪辑中&#xff0c;经常要对多个视频片段进行时长调整和播放倍速的修改。如果一个个手动操作&#xff0c;不仅效率低下&#xff0c;还容易出错。如何快速批量处理这些片段呢&#xff1f;现在一起来看看云炫AI智剪批量减片时长并调整播放的具体步骤。 原视频和剪辑后的视…

【国内访问github不稳定】可以尝试fastgithub解决这个问题

1、下载 https://github.com/dotnetcore/FastGithub https://github.com/dotnetcore/FastGithub/releases 官网下载即可&#xff0c;比如&#xff0c;我用的是这个&#xff1a;fastgithub_osx-x64.zip&#xff08;点这里下载&#xff09; 2、安装 如下图双击启动即可 3、…

Android14实战:打破音频默认重采样的限制(五十二)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

PMP学习考试经验总结

PMP备考日程计划表 我的PMP的备考大概花了三个月的时间, 可以分为以下几个阶段&#xff1a; Week 1-4: 读完PMBoK 前面7个知识领域&#xff08;中英文版PMBoK一起看&#xff09;。每看完一个知识领域&#xff0c;就看参考书里面的相应章节&#xff08;汪博士那本&#xff09;…