当前位置: 首页 > news >正文

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

一、内容分析

我们准备开发一个针对全网内容的搜索引擎,产品名称为“Bingoo”。

Bingoo的主要技术挑战包括:

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

12.1 概要设计

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

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

每个网页被分配得到一个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值进行排名,最终呈现给用户,保证用户最先看到的网页是最接近用户期望的结果页面。

12.2 详细设计

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

12.2.1 索引

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

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

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

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

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

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

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

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

架构->1,2,4

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

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

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

同时遍历两个链表,如果其中一个链表当前指向的元素小于另一个链表当前指向的元素,那么这个链表就继续向前遍历;如果两个链表当前指向的元素相同,该元素就是交集元素,记录在结果列表中;依此继续向前遍历,直到其中一个链表指向自己的尾部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千万长度的链表求交集,最终的结果可能不过几万,也就是说,大部分的比较都是不相等的。比如下面的例子。

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

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

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

12.2.2 PageRank排名算法

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

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

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

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

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分。用公式表示就是

\(\\small PR(A) = \\frac{PR(B)}{2}+\\frac{PR(C)}{3}+\\frac{PR(D)}{1}\)

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

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

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

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

\(\\small PR(A) = \\alpha(\\frac{PR(B)}{2}+\\frac{PR(C)}{3}+\\frac{PR(D)}{1})+\\frac{(1-\\alpha)}{4}\)

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

那么对于N个网页,任何一个页面\(\\small P_{i}\)的PageRank计算公式如下:

\(\\small PageRank(P_{i})=\\alpha \\sum_{P_{j}\\in M(P_{i})}^{}{\\frac{PageRank(P_{j})}{L(P_{j})}} + \\frac{1-\\alpha}{N}\)

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

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

二、粉丝福利

我根据我从小白到架构师多年的学习经验整理出来了一份50W字面试解析文档、简历模板、学习路线图、java必看学习书籍 、 需要的小伙伴斯我一下,或者评论区扣“求分享

相关文章:

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

一、内容分析 我们准备开发一个针对全网内容的搜索引擎,产品名称为“Bingoo”。 Bingoo的主要技术挑战包括: 针对爬虫获取的海量数据,如何高效地进行数据管理;当用户输入搜索词的时候,如何快速查找包含搜索词的网页…...

【Python】ModuleNotFoundError: No module named ‘distutils.util‘ bug fix

【Python】ModuleNotFoundError: No module named distutils.util bug fix 1. error like this2. how to fix why this error occured , because i remove the origin version python of ubuntu of 20.04. then the system trapped in tty1 , you must make sure the laptop li…...

痉挛性斜颈对生活有哪些影响?

痉挛性斜颈,这个名字听起来可能并不熟悉,但它实际上是一种神经系统疾病,影响着全球数百万人的生活质量。它以一种无法控制的方式,使患者的颈部肌肉发生不自主的收缩,导致头部姿势异常。对于患者来说,痉挛性…...

Javassist 修改 jar 包里的 class 文件

前言 Javassist 是一个用于处理 Java 字节码的类库,可以用以修改 class 文件或 jar 包里的 class 文件。 简单来说我们用Java编写的代码是放在 java 格式的代码文件里,在编译的时候会编译为 class 格式的字节码文件,然后一般所有 class 文件…...

交换机的二三层原理

相同VLAN的交换机交换原理(二层交换原理): 交换机收到数据帧,首先会检查数据帧的VLAN标签和目标MAC,若属于相同VLAN,且该目标MAC在本地MAC表中,则直接根据出接口进行数据转发 不同VLAN的交换机…...

HarmonyOS ArkUi 字符串<展开/收起>功能

效果图: 官方API: ohos.measure (文本计算) 方式一 measure.measureTextSize 跟方式二使用一样,只是API调用不同,可仔细查看官网方式二 API 12 import { display, promptAction } from kit.ArkUI import { MeasureUtils } fr…...

Lianwei 安全周报|2024.07.09

新的一周又开始了,以下是本周「Lianwei周报」,我们总结推荐了本周的政策/标准/指南最新动态、热点资讯和安全事件,保证大家不错过本周的每一个重点! 政策/标准/指南最新动态 01 《数字中国发展报告(2023年&#xff09…...

火遍全网的15个Python的实战项目,你该不会还不知道怎么用吧!

经常听到有朋友说,学习编程是一件非常枯燥无味的事情。其实,大家有没有认真想过,可能是我们的学习方法不对? 比方说,你有没有想过,可以通过打游戏来学编程? 今天我想跟大家分享几个Python小游…...

快速使用BRTR公式出具的大模型Prompt提示语

Role:文章模仿大师 Background: 你是一位文章模仿大师,擅长分析文章风格并进行模仿创作。老板常让你学习他人文章后进行模仿创作。 Attention: 请专注在文章模仿任务上,提供高质量的输出。 Profile: Author: 一博Version: 1.0Language: 中文Descri…...

Xilinx FPGA DDR4 接口的 PCB 准则

目录 1. 简介 1.1 FPGA-MIG 与 DDR4 介绍 1.2 DDR4 信号介绍 1.2.1 Clock Signals 1.2.2 Address and Command Signals 1.2.3 Control Signals 1.2.4 Data Signals 1.2.5 Other Signals 2. 通用存储器布线准则 3. Xilinx FPGA-MIG 的 PCB 准则 3.1 引脚配置 3.1.1 …...

神经网络 | Transformer 基本原理

目录 1 为什么使用 Transformer?2 Attention 注意力机制2.1 什么是 Q、K、V 矩阵?2.2 Attention Value 计算流程2.3 Self-Attention 自注意力机制2.3 Multi-Head Attention 多头注意力机制 3 Transformer 模型架构3.1 Positional Encoding 位置编…...

浅析 VO、DTO、DO、PO 的概念

文章目录 I 浅析 VO、DTO、DO、PO1.1 概念1.2 模型1.3 VO与DTO的区别I 浅析 VO、DTO、DO、PO 1.1 概念 VO(View Object) 视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。DTO(Data Transfer Object): 数据传输对象,这个概念来源于J2EE的设…...

7.8 CompletableFuture

Future 接口理论知识复习 Future 接口(FutureTask 实现类)定义了操作异步任务执行的一些方法,如获取异步任务的执行结果、取消任务的执行、判断任务是否被取消、判断任务执行是否完毕等。 比如主线程让一个子线程去执行任务,子线…...

iPad锁屏密码忘记怎么办?有什么方法可以解锁?

当我们在日常使用iPad时,偶尔可能会遇到忘记锁屏密码的尴尬情况。这时,不必过于担心,因为有多种方法可以帮助您解锁iPad。接下来,小编将为您详细介绍这些解决方案。 一、使用iCloud的“查找我的iPhone”功能 如果你曾经启用了“查…...

了解并缓解 IP 欺骗攻击

欺骗是黑客用来未经授权访问计算机或网络的一种网络攻击,IP 欺骗是其他欺骗方法中最常见的欺骗类型。通过 IP 欺骗,攻击者可以隐藏 IP 数据包的真实来源,使攻击来源难以知晓。一旦访问网络或设备/主机,网络犯罪分子通常会挖掘其中…...

java LogUtil输出日志打日志的class文件内具体方法和行号

最近琢磨怎么把日志打的更清晰,方便查找问题,又不需要在每个class内都创建Logger对象,还带上不同的颜色做区分,简直不要太爽。利用堆栈的方向顺序拿到日志的class问题。看效果,直接上代码。 1、demo test 2、输出效果…...

02. Hibernate 初体验之持久化对象

1. 前言 本节课程让我们一起体验 Hibernate 的魅力!编写第一个基于 Hibernate 的实例程序。 在本节课程中,你将学到 : Hibernate 的版本发展史;持久化对象的特点。 为了更好地讲解这个内容,这个初体验案例分上下 2…...

MySQL超详细学习教程,2023年硬核学习路线

文章目录 前言1. 数据库的相关概念1.1 数据1.2 数据库1.3 数据库管理系统1.4 数据库系统1.5 SQL 2. MySQL数据库2.1 MySQL安装2.2 MySQL配置2.2.1 添加环境变量2.2.2 新建配置文件2.2.3 初始化MySQL2.2.4 注册MySQL服务2.2.5 启动MySQL服务 2.3 MySQL登录和退出2.4 MySQL卸载2.…...

初识SpringBoot

1.Maven Maven是⼀个项⽬管理⼯具, 通过pom.xml⽂件的配置获取jar包,⽽不⽤⼿动去添加jar包 主要功能 项⽬构建管理依赖 构建Maven项目 1.1项目构建 Maven 提供了标准的,跨平台(Linux, Windows, MacOS等)的⾃动化项⽬构建⽅式 当我们开发了⼀个项⽬之后, 代…...

Qt之元对象系统

Qt的元对象系统提供了信号和槽机制(用于对象间的通信)、运行时类型信息和动态属性系统。 元对象系统基于三个要素: 1、QObject类为那些可以利用元对象系统的对象提供了一个基类。 2、在类声明中使用Q_OBJECT宏用于启用元对象特性&#xff0c…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...