首页 > 其他分享 >搜索引擎索引基础知识分享

搜索引擎索引基础知识分享

时间:2024-09-05 10:52:11浏览次数:4  
标签:Term buy car term 搜索引擎 索引 文档 基础知识

一. 为什么不使用MySQL?
MySQL只适用于结构化数据的检索,而不适用于全文检索,全文检索简单来说就是要在大量文档中找出包含某个单词的所有文档
那么什么叫做全文检索呢?
数据总体分为:
● 结构化数据:指具有固定格式或有限长度的数据,如数据库,元数据等。
● 非结构化数据:指不定长或无固定格式的数据,如邮件,word文档等。
● 半结构化数据:如XML,HTML等,根据需要可按结构化数据来处理,也可抽取出纯文本按非结构化数据来处理。
非结构化数据又一种叫法叫全文数据。

比如我们想从海量的商品数据中,想检索包含“自行车”的所有商品,在MySQL中可能会想用like:
select * from item where name like '%自行车%'
但是熟悉MySQL innoDB存储引擎的同学都知道,由于B+树索引是是按照「索引值」有序排列存储的,只能根据前缀进行比较,也就是最左匹配原则,所以这个左右模糊匹配不会走索引,也就是会全表扫描,查询效率就会很低
此外,使用MySQL的缺点有:
● 搜索效果差,只能首尾位模糊匹配,无法实现复杂的搜索需求
● 无法使用数据库索引,需要全表扫描,性能差
● 无法得到文档与搜索条件的相关性
所以需要创建一种高效的索引结构来支持全文检索。

二. 如何创建索引
传统上,我们去查找一篇文章中的某个单词,这个过程是文档-->单词的映射,而我们现在的需求是:想在海量文档中,找出包含特定单词的所有文档,也即已知单词,欲求文档,也即从单词-->文件的映射,两者恰恰相反。于是如果索引总能够保存从字符串到文件的映射,则会大大提高搜索速度。
由于从单词-->文件的map是文档-->单词的map的反向过程,于是保存这种信息的索引称为反向索引,也是倒排索引。
目前主流的索引方式都是倒排索引,倒排索引是区别于正排索引的概念:
● 正排索引:是以文档对象的唯一 ID 作为索引,以文档内容作为记录的结构。
● 倒排索引:Inverted index,指的是将文档内容中的单词作为索引,将包含该词的文档 ID 作为记录的结构。

因此,要构建索引,首先需要处理我们的文档,真实情况下涉及自然语言处理(NLP),这里简单说一下过程:

假设我们有两个文档(Documents):
文档一:This is some nice cars, My friend want to buy one car.
文档二: My friend drove me to buy it in his red car
(1)分词-Tokenize
分词主要做以下工作:

  1. 将文档分成一个一个单独的单词。
  2. 去除标点符号。
  3. 去除停词(Stop word)。停词(Stop word)指语言中最普通的一些单词,没有特别的意义,大多数情况下不能成为搜索的关键词,如“the”, “a”,“this”等。
    分词处理完的结果称为Token,比如在本例中处理完的结果为:
    nice,cars,want,buy,My,friend,My,friend,drove,me,buy,his,red,car
    (2)词项处理-Linguistic Processor
    这一步骤主要做以下工作:
  4. 归一化:将所有英语单词转小写,并且归一
    ○ USA - U.S.A. [缩写]
    ○ 7月30日 - 7/30 [中英文]
    ○ color - colour [通假词]
    ○ 开心 - 高兴 [同义词扩展范畴]
  5. 词形归并:比如car, cars, car's, cars' -> car
  6. 词根还原:比如driven,drove,driving -> drive
    经过上面处理后的结果称为Term:
    nice,car,want,buy,friend,drive,buy,he,red, car, my
    (3)索引构建-Indexing
    这一步骤主要做以下工作:
  7. 利用得到的词(Term)创建一个字典。
  8. 对字典按字母顺序进行排序。
  9. 合并相同的词(Term)成为文档倒排(Posting List)链表。
    Term Doc id
    buy 1, 2
    car 1, 2
    drive 2
    friend 1,2
    he 2
    my 1,2
    nice 1
    red 2
    want 1

根据倒排索引的概念,我们可以用一个 Map来简单描述这个结构。这个 Map 的 Key 的即是分词后的单词Term,这一系列的 Term 组成了倒排索引的第一个部分 —— Term Dictionary (索引表,可简称为 Dictionary)。
倒排索引的另一部分为 Postings List(记录表),也对应上述 Map 结构的 Value 部分集合。
除此之外,在此表中,有几个定义:
● Document Frequency 即文档频次,表示总共有多少文件包含此词(Term)。
● Frequency 即词频率,表示此文件中包含了几个此词(Term)。
所以对词(Term) “car”来讲,总共有两篇文档包含此词(Term),从而词(Term)后面的文档链表总共有两项,第一项表示包含“car”的第一篇文档,即1号文档,此文档中,“car”出现了2次,第二项表示包含“car”的第二个文档,是2号文档,此文档中,“car”出现了1次,到此为止,索引已经创建好了。

三. 如何进行搜索
搜索主要分为以下几步:
第一步:用户输入查询语句。
举个例子,用户输入语句:car AND buy NOT drive。
说明用户想找一个包含car和buy然而不包括drive的文档。

第二步:词法分析&语法分析
主要是识别token和关键字,经过词法分析后:
token:car, drive, buy
关键字:AND NOT
构建语法树:

第三步:搜索索引,得到符合语法树的文档。
此步骤有分几小步:

  1. 首先,在反向索引表中,分别找出包含car,drive,buy的文档链表。

  2. 其次,对包含car,buy的链表进行合并操作,得到既包含car又包含buy的文档链表。

  3. 然后,将此链表与drive的文档链表进行差操作,去除包含drive的文档,从而得到既包含car又包含buy而且不包含drive的文档链表。

  4. 此文档链表就是我们要找的文档:doc1。

第四步:根据得到的文档和查询语句的相关性,对结果进行排序。
向量空间模型的算法(Vector Space Model)

  1. 计算权重(Term weight)的过程。
    影响一个词(Term)在一篇文档中的重要性主要有两个因素:
    ● Term Frequency (tf):即此Term在此文档中出现了多少次。tf 越大说明越重要。
    ● Document Frequency (df):即有多少文档包含次Term。df 越大说明越不重要。
    term的权重公式为:
    $w_{t, d}=t f_{t, d} \times \log \left(n / d f_{t}\right)$

● w(t,d)是term t在文档d 中的权重
● tf(t,d)是term t在文档d中的出现次数
● n是总文档数
● df(t)是包含term t的文档数

  1. 相关性打分
    文档由一系列词(Term)组成,每一个词(Term)都有一个权重(Term weight),不同的词(Term)根据自己在文档中的权重来影响文档相关性的打分计算。
    我们把所有此文档中词(term)的权重(term weight) 看作一个向量。
    $Document = <term1, term2, …… ,term N>$
    $Document Vector = <weight1, weight2, …… ,weight N>$
    同样我们把查询语句看作一个简单的文档,也用向量来表示。
    $Query = <term1, term 2, …… , term N>$
    $Query Vector = <weight1, weight2, …… , weight N>$
    我们把所有搜索出的文档向量及查询向量放到一个N维空间中,每个词(term)是一维。

通过 计算夹角的余弦值作为相关性的打分,夹角越小,余弦值越大,打分越高,相关性越大。
相关性打分公式为
$\operatorname{score}(q, d)=\frac{\vec{V}{q} \cdot \vec{V}{d}}{\left|\vec{V}{q}\right|\left|\vec{V}\right|}=\frac{\sum_{i=1}^{n} w_{i, q} w_{i, d}}{\sqrt{\sum_{i=1}^{n} w_{i, q}^{2}} \sqrt{\sum_{i=1}^{n} w_{i, d}^{2}}}$

然后返回相关性最高的文档给用户即可。向量空间模型采用统计学方法,将文档和查询信息表示为向量,实现了相关性排序,但是它的缺点是假设了关键词之间彼此独立,实际上用户一个搜索请求背后的逻辑往往不是这么简单。比如还有BM25算法
$\operatorname{Score}(Q, d)=\sum_{i}^{n} \operatorname{IDF}\left(q_{i}\right) \cdot \frac{f_{i} \cdot\left(k_{1}+1\right)}{f_{i}+k_{1} \cdot\left(1-b+b \cdot \frac{d l}{\text { avgdl }}\right)}$

四. 空间索引
GeoHash
如何知道离自己50m的范围内的所有餐馆?
传统暴力做法:使用精度与纬度,利用球面距离公式,计算地球上所有POI与自己的距离,然后保留距离<50m的POI

GeoHash,根据经纬度进行二分,然后编码,举例北海公园的经纬度是(39.928167, 116.389550)
以纬度为例,经度不再赘述:
bit min mid max
1 -90.000 0.000 90.000
0 0.000 45.000 90.000
1 0.000 22.500 45.000
1 22.500 33.750 45.000
1 33.7500 39.375 45.000
0 39.375 42.188 45.000
0 39.375 40.7815 42.188
0 39.375 40.07825 40.7815
1 39.375 39.726625 40.07825
1 39.726625 39.9024375 40.07825
通过上述计算,
纬度产生的编码为10111 00011,
经度产生的编码为11010 01011。
偶数位放经度,奇数位放纬度,把2串编码组合生成新串:11100 11101 00100 01111。

最后使用用0-9、b-z(去掉a, i, l, o)这32个字母进行base32编码,首先将11100 11101 00100 01111转成十进制,对应着28、29、4、15,十进制对应的编码就是wx4g。

● GeoHash将二维的经纬度转换成字符串
● 字符串越长,表示的范围越精确
● 字符串相似的表示距离相近

KD Tree
KD Tree是 k-Dimension Tree,以k=2,2维举例。
设想一下,平面上有100万个点,给定一个特定点,如何求距离这个点最近的100个点?
传统做法:
同样暴力求解

KD Tree做法:
二维样例:{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)}

首先对数据按X轴排序,取中位数划分,会有一些点落在左边分支/右边分支
然后对数据按Y轴排序,取中位数划分......
......
直到每个空间只剩1个点 / 0个点,然后建树

最邻近检索的步骤为:

  1. 从根节点开始,X层比较x坐标,Y层比较y坐标,类似二叉树搜索,直至叶节点
  2. 记录叶节点的欧式距离d,然后回溯,直至X,Y均大于d
  3. 回溯主要是,当前节点所在切分轴与对应的坐标的距离如果小于d,那么很有可能在另一个分支存在更近的点,搜索另一个分支,若存在,更新d

举个例子,查找(6,3.5)

  1. 先查到(2,3),计算d = 4.03
  2. 回溯到(5,4),y轴距离 = 4 - 3.5 = 0.5 < d,说明下面(包含)可能存在距离更近的,计算当前点(5,4)的距离,d = 1.18 < 4.03 更新d
  3. 回溯到 (7,2),x轴距离 = 7 - 6 = 1 < d,右侧可能存在更近,计算当前点(7,2)的距离,d = 1.802 > 1.18,右侧不存在,结束搜索。

五. 优化
以开源搜索引擎Lucene为例

数据压缩
PackedBlock
其采用 PackedInts 结构将一个 int[] 压缩打包成一个紧凑的 Block。它的压缩方式是取数组中最大值所占用的 bit 长度作为一个预算的长度,然后将数组每个元素按这个长度进行截取,以达到压缩的目的。
例如:一个包含128个元素的 int 数组中最大值的是 2,那么预算长度为2个 bit, PackedInts 的长度仅是 2 * 128 / 8 = 32个字节,然后就可以通过4个 long 值存储。

VIntBlock
VIntBlock 是采用 VInt 来压缩 int 值,对于绝大多数语言,int 型都占4个字节,不论这个数据是1、100、1000、还是1000,000。VInt 采用可变长的字节来表示一个整数。数值较大的数,使用较多的字节来表示,数值较少的数,使用较少的字节来表示。每个字节仅使用第1至第7位(共7 bits)存储数据,第8位作为标识,表示是否需要继续读取下一个字节。
举个例子:
整数130为 int 类型时需要4个字节,转换成 VInt 后仅用2个字节,其中第一个字节的第8位为1,标识需要继续读取第二个字节。

SkipData
为了能够快速查找docid,lucene采用了SkipList这一数据结构。SkipList有以下几个特征:

  1. 元素排序的,对应到我们的倒排链,lucene是按照docid进行排序,从小到大;
  2. 跳跃有一个固定的间隔,这个是需要建立SkipList的时候指定好,例如下图以间隔是;
  3. SkipList的层次,这个是指整个SkipList有几层

时间复杂度log(n)

FST
使用lucene进行查询不可避免都会使用到其提供的字典功能,即根据给定的term找到该term所对应的倒排文档id列表等信息。
怎么实现一个字典呢?我们马上想到排序数组,即term字典是一个已经按字母顺序排序好的数组,数组每一项存放着term和对应的倒排文档id列表。每次载入索引的时候只要将term数组载入内存,通过二分查找即可。排序数组的缺点是消耗内存,即需要完整存储每一个term,当term数目多达上千万时,占用的内存将不可接受。

lucene从4开始大量使用的数据结构是FST(Finite State Transducer)。FST有两个优点:
1)空间占用小。通过对词典中单词前缀和后缀的重复利用,压缩了存储空间;
2)查询速度快。O(len(str))的查询时间复杂度。

其实和Tries(字典树)我想是类似的
KDB Tree
KDB-tree(k-dimensional B-tree)是一个用于划分K维搜索空间的树形结构,KDB-tree的既提供平衡KD树的搜索效率,又有B树面向块的存储来优化外部内存的访问效率。

结构
KDB-tree包括两种类型的页:
● 区域页面: (region, child)对的集合包含边界区域的描述,加上该区域指向子页面的指针。
● 点页面:(point, location)对的集合。数据库方面,location可能指向数据库记录的索引,对于K维空间中的点,可以被看成该空间中的点坐标。

六. 进一步学习
(1) ha3还有更多索引:截断索引 ,实时索引 ,向量索引
达摩院向量检索Proxima
FaceBook Faiss

(2) indexlib--KKV 索引
KKV模型(PKey-SKey-Value)通常用来存储属性图中的关系实例(如图1中的好友关系),其中PKey(primary key)存储一条边的源节点ID(上图中节点U1),SKey(secondary key)存储一条边的目标节点ID(比如上图中节点U2/U3/U4),Value用来存放关系的属性(比如好友关系的time和score属性)。

(3) 更多检索技术

七. 参考文献
GeoHash
KD Tree
浅谈搜索引擎

标签:Term,buy,car,term,搜索引擎,索引,文档,基础知识
From: https://www.cnblogs.com/fallenpetal/p/18397958

相关文章

  • Python基础知识-8(PyQt GUI开发,输出乱码处理)
    (目录)介绍一个VSCode轻量级RestAPI客户端插件:ThunderClient一、关于shebang明确指定解释器#!/usr/bin/python3在shell中寻找第一个python解释器#!/usr/bin/envpython3二、Python类的私有方法/属性Python不支持私有方法/属性,但可以将类成员方法/属性名定义为......
  • java基础知识-JVM知识详解
    一、JVM内存结构Java虚拟机(JVM)的内存结构主要分为几个不同的区域,每个区域都有其特定的目的和功能。以下是JVM内存结构的主要组成部分:先看一下总体的结构图程序计数器(ProgramCounterRegister)这是一个较小的内存块,用于存储当前线程所执行的字节码指令的地址。每......
  • 【分立元件】电阻的基础知识
            电阻与电容、电感一样都是最基本的元器件,大量使用于各种电气或电子设备中。对从事电气工作的人而言或许过于普通,平时忽视了它,但如果没有电阻,电气或电子电路就无法建立。电阻就是如此重要的元器件。 电阻的原理        电阻的数值取决于电阻材料的电......
  • IP地址基础知识科普
    IP地址是分配给连接到互联网上的每一台设备的唯一数字标识,用于网络之间相互连通。在互联网上,只有输入正确的IP地址,才能获得准确的信息。通常IP地址在计算机网络中用数字形式体现。IP地址的构成通常IP地址是由网络地址和主机地址两部分构成的。网络地址:用于标识某个IP地址所......
  • LLM大模型基础知识学习总结
    大家好,我是Edison。在这个已经被大模型包围的时代,不了解一点大模型的基础知识和相关概念,可能出去聊天都接不上话。刚好近期我也一直在用GPT和GitHubCopilot,也刚好对这些基础知识很感兴趣,于是学习了一下,做了如下的整理总结,分享与你!一句话描述GPTGPT:GenerativePre-TrainingTra......
  • kafka基础知识(持续更新中~)
    #broker.id属性在kafka集群中必须要是唯⼀broker.id=0#kafka部署的机器ip和提供服务的端⼝号listeners=PLAINTEXT://192.168.65.60:9092#kafka的消息存储⽂件log.dir=/usr/local/data/kafka-logs#kafka连接zookeeper的地址zookeeper.connect=192.168.65.60:2181./......
  • 基础知识内容
    基础知识预览if语句注意结合and和or句式1ifcon1thenpasselseifcon2thenpasselseifcon3thenpasselsespassendif句式2ifconthenpasselsepassendif句式3+句式4ifcon1thenresult1ifcon1thenresult_Yelse......
  • C++入门基础知识48——【关于C++函数】之Lambda 函数与表达式
    成长路上不孤单......
  • 使用LXR搭建Linux Kernel源码索引服务器
    0.测试环境Ubuntu13.10(64位,Kernel为自己编译的3.13.6)1.工具a.Perl在我的Ubuntu里已安装了Perl,版本信息如下:Thisisperl5,version14,subversion2(v5.14.2)builtforx86_64-linux-gnu-thread-multib.ctags使用sudoapt-getinstallctags进行安装,我现在安装好后......
  • MySQL索引底层实现原理
    索引的本质MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。提取句子主干,就可以得到索引的本质:索引是数据结构。我们知道,数据库查询是数据库的最主要功能之一。我们都希望查询数据的速度能尽可能的快,因此数据库系统的设计者会从查询算法的角度进行优化。最基......