【文档搜索引擎】缓冲区优化和索引模块小结

ops/2024/12/26 9:04:02/

开机之后,首次制作索引会非常慢,但后面就会快了
重启机器,第一次制作又会非常慢
这是为什么呢?

在 parserContent 里面,我们进行了一个读文件的操作

  • 计算机读取文件,是一个开销比较大的操作,

缓存
parserContent 的核心操作,就是读取文件,从磁盘进行访问,操作系统就会对“经常读取的文件”进行缓存
首次运行的时候,当前的这些 Java 文档,都没有在内存中缓存,因此读取的时候只能直接从硬盘上读取(相对耗时)
后面再运行的时候,由于前面已经读取过这些文档了,这些文档都在操作系统中其实已经有了一份缓存(在内存中),这次的读取不必直接读取硬盘,而是直接读内存的缓存(速度就会快很多)

缓冲区优化

我们可以通过使用一些线程类,来将缓存加进去,提高读取速率。

我们使用的读文件操作是:

java">int ret = fileReader.read();
  • 这里的 read() 每次都是在读磁盘,速度就会比较慢
  • 相比之下,我们可以在这里将 fileReader 的内容提前就读取到内存之中,然后每次调用 read() 的时候,就可以直接从内存中进行读取了

BufferedReader

BufferedReader 可以搭配 FileReader 来使用。我们只需要在构造 BufferedReader 的时候,把 FileReader 实例给设置进去就可以了

  • BufferedReader 内部就会内置一个缓冲区,就能够自动的把 FileReader 中的一些内容预读到内存中,从而减少直接访问磁盘的次数
java">BufferedReader bufferedReader = new BufferedReader(new FileReader(f))

image.png|498
通过 BufferedReader 类可以看到,它的缓冲区默认值是 8192(8k),我们可以将其设置大一点。我们就将 BufferedReader 的第二个参数,手动设置一下大小

java">BufferedReader bufferedReader = new BufferedReader(new FileReader(f), 1024 * 1024)
  • 手动将缓冲区设置为 1M 大小

索引加载逻辑

image.png|544

我们的索引加载逻辑,就是要从这两个文件进行加载。这里的文件保存好的索引数据,再给它保存到内存中,把它还原成内存中的那两个数据结构

java">// 正排索引
private ArrayList<DocInfo> forwardIndex = new ArrayList<>();  // 倒排索引
private HashMap<String, ArrayList<Weight>> invertedIndex = new HashMap<>();

我们可以验证一下这个索引加载

java">public static void main(String[] args) {  Index index = new Index();  index.load();  System.out.println("索引加载完毕!");  
}
  • 我们将断点打到打印那一行,然后观察调试信息 image.png|552
  • 可以看到两个数据结构都被还原到内存中了

索引模块小结

1. Parser 类

作用

  1. 针对递归的方式,枚举除了所有的 HTML 文件

  2. 针对这里的每个 HTML 进行解析

    • 标题:直接使用的文件标题
    • URL:基于文件路径进行简单的拼接(离线文档和线上文档的路径关系)
    • 正文:核心操作——去标签(将这里的 HTML 标签都去掉)。简单粗暴的方式实现,使用 <> 作为“是否要拷贝数据”的开关
  3. 将这里的解析结果,放到 Index 类中(addDoc 方法)

通过这个 Parser 类,最主要的事情,还是辅助 Index 类,来完成索引制作的过程

Parser 类主要就是:

  1. 做准备工作
  2. 调用 Index
  3. 通过这个类,作为完整应用程序的入口类
    后续需要制作索引,就直接调用 Parsermain 方法即可

单线程 vs 多线程

单线程制作索引比较低效,速度比较慢。改进成多线程之后,速度就明显有了提升。

我们要明确地描述出,这些文档什么时候能处理完。如果没处理完,我们是不能轻易地保存索引的,必须得保证所有的文档都解析完毕了、在索引中加载完毕了,才能够真正地保存在文件当中。

  • 所以我们使用 CountDownLatch 来计数。只有当所有的文档都处理完毕,都已经调用了 countDown 方法,撞线了,然后我们才能执行保存索引的操作

读文件缓冲区

在 Parser 类中,涉及到大量的读文件操作,我们通过实验得出:首次加载索引的时候速度会慢一些,后面再读就会变快了。

  • 因为第一次读的时候,这些文档都在磁盘上,内存中没有缓存,所以我们读的速度就会慢一些
  • 读过一次之后,很多文件就会操作系统自动缓存。之后我们再加载索引的时候,这里的文档就不用都在磁盘中读了,相当一部分可直接在内存中读取,这样速度就会变快

2. Index 类

核心属性

  1. 正排索引
java">private ArrayList<DocInfo> forwardIndex = new ArrayList<>();
  • 每个 DocInfo 都表示一个文档,在这个文档里面就包含了 idtitleurlcontent
  1. 倒排索引
java">private HashMap<String, ArrayList<Weight>> invertedIndex = new HashMap<>();
  • 每个键值对,就表示这个词在哪些文档中出现过
  • Weight 不仅仅是包含了文档 id,还包含了权重信息。权重当前是通过词出现的频次来计算的(标题中出现的次数 * 10 + 正文中出现的次数

核心方法

  1. 查正排
    直接按照下标来取 ArrayList<DocInfo> 中的元素即可

  2. 查倒排
    直接按照 key 来取 HashMap<String, ArrayList<Weight>>value 即可

  3. 添加文档
    通过 Parser 类,在构建索引的时候,调用该方法

  • 构建正排,构造一个 DocInfo 对象,给其添加到正排索引末尾
  • 构建倒排,先进行分词,统计词频,遍历分词结果,去更新倒排索引中对应的倒排拉链(注意其中的线程安全问题)
  1. 保存索引
    基于 JSON 格式,把索引数据保存到指定文件中

  2. 加载索引
    基于 JSON 格式,对数据进行解析。把文件中的内容读出来,解析到内存中


http://www.ppmy.cn/ops/145080.html

相关文章

如何查看pad的console输出,以便我们更好的进行调试,查看并了解实际可能的问题。

1、以下是baidu AI回复&#xff1a; 2、说明&#xff1a; 1&#xff09;如果小伙伴们经常做android开发的话&#xff0c;这个不陌生&#xff0c;因为调试都是要开启这个开发者模式。并启用USB调试模式。 2&#xff09;需要连上USB线&#xff0c;有的时候会忘记&#xff0c;然…

代码随想录算法训练营第51期第28天 | 122. 买卖股票的最佳时机 II、55. 跳跃游戏、45. 跳跃游戏 II、1005.K次取反后最大化的数组和

122. 买卖股票的最佳时机 II 122. 买卖股票的最佳时机 IIhttps://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/1.我刚刚看了一下之前用C写题的时候&#xff0c;自己说了句【我好像记得这道题是怎么写的&#xff0c;也不知道是福是祸】会心一笑&#xff0c;好像不…

活力笔记:一款让你的灵感永不落幕的应用

今天我要向大家介绍一个超级酷炫的项目——VividNote&#xff08;活力笔记&#xff09;。它不仅仅是一个简单的笔记应用&#xff0c;更像是你口袋里的创意伙伴。想象一下&#xff0c;如果你能随时随地捕捉那些一闪而过的灵感&#xff0c;并将它们整理成有序的知识库&#xff0c…

Web 漏洞之 CSRF 漏洞挖掘:攻防深度剖析

目录 一、引言 二、CSRF 漏洞的概念 三、攻击者视角下的 CSRF 漏洞挖掘与利用 &#xff08;一&#xff09;攻击原理与条件 &#xff08;二&#xff09;漏洞分类及利用方式 &#xff08;三&#xff09;漏洞发现手法 &#xff08;四&#xff09;高级应用场景及绕过方法 四…

第6章 图论

2024年12月25日一稿 &#x1f430;6.1 图的基本概念 6.1.1 图的定义和表示 6.1.2 图的同构 6.1.3 完全图与正则图 6.1.4 子图与补图 6.1.5 通路与回路 6.2 图的连通性 6.2.1 无向图的连通性 6.2.2 有向图的连通性 6.3 图的矩阵表示 6.3.1 关联矩阵 6.3.2 有向图的邻接矩阵…

C++进阶-1-单继承、多继承、虚继承

C单继承详解 1. 基础概念 继承是面向对象编程中的一个核心概念&#xff0c;允许一个类&#xff08;子类或派生类&#xff09;继承另一个类&#xff08;父类或基类&#xff09;的属性和方法。单继承意味着一个类只能直接继承一个父类。这种简单的结构在许多情况下是足够的&…

鲸鱼机器人和乐高机器人的比较

鲸鱼机器人和乐高机器人各有其独特的优势和特点&#xff0c;家长在选择时可以根据孩子的年龄、兴趣、经济能力等因素进行综合考虑&#xff0c;选择最适合孩子的教育机器人产品。 优势 鲸鱼机器人 1&#xff09;价格亲民&#xff1a;鲸鱼机器人的产品价格相对乐高更为亲民&…

Flutter 实现文本缩放学习

Flutter 如何实现一个简单的文本缩放应用程序&#xff0c;其中包含一个可以增加或减少文本大小的功能。 前置知识点学习 TextScaler TextScaler 是一个用于控制文本缩放的工具或机制&#xff0c;不过需要注意的是&#xff0c;TextScaler 并不是 Flutter 框架中内置的类。在 …