9、Solr 检索工具

1、Solr 简介

Solr 是一个高性能,基于 Lucene 的全文检索服务,也可以作为 NoSQL 数据库使用。Solr 对 Lucene 进行了扩展,提供了比 Lucene 更为丰富的查询语言,同时实现了可配置、可扩展,并对查询性能进行了优化,还提供了一个完善的功能管理界面。

SolrCloud 从 Solr 4.0 版本基于 Solr 和 Zookeeper 进行开发,具有开创意义的分布式索引和搜索方案。

应用场景:

 

待检索的数据类型复杂:如需要查询的数据有结构化数据(关系型数据库等)、半结构化数据(网页、XML 等)、非结构化数据(日志、图片、图像等)等,Solr可以对以上数据类型进行清洗、分词、建立倒排索引等一系列操作(建立索引),然后提供全文检索(查询)的能力。

检索条件多样化(如涉及字段太多),常规查询无法满足:全文检索(查询)可以包括词和短语,或者词或短语的多种形式。

读数据取远多于写入数据的应用场景。

在实际生活中,我们也需要使用到大量的检索服务才可以正常的执行相关的工作,比如春运买票,作为 12306 的系统,我们在买票的时候,后台需要首先检索出我们的数据,之后检查我们的购票记录,然后针对于我们的购票行为还要添加新的 数据到数据库中。那么在 12306 后台数据库中,用户十几亿,如何快速的检索出一条数据这就是我们所需要关注的问题了。 

Solr 提供的功能分为:索引和查询。

索引数据涉及的内容:数据来源、索引存储以及索引数据导入方式。

(1)数据来源可以从 DB、HBase、HDFS 等组件的数据导入而来。

(2)索引存储方式 可以选择为存储于本地或者 HDFS。

(3)索引方式 可以采用 MR 的任务进行批量导入及使用客户端 API 实时导入。在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。索引提供指向存储在表的指定列中的数据值的指针,然后根据您指定的排序顺序对这些指针排序。数据库使用索引以找到特定值,然后顺指针找到包含该值的行。这样可以使对应于表的 SQL 语句执行得更快,可快速访问数据库表中的特定信息。当表中有大量记录时,若要对表进行查询,第一种搜索信息方式是全表搜索,是 将所有记录一一取出,和查询条件进行一一对比,然后返回满足条件的记录,这 样做会消耗大量数据库系统时间,并造成大量磁盘 I/O 操作;第二种就是在表中建立索引,然后在索引中找到符合查询条件的索引值,最后通过保存在索引中的 ROWID(相当于页码)快速找到表中对应的记录。

在实际的操作中,我们如果需要做到快速检索数据,首先第一步需要做到的就是建立索引,之后才能做对应的数据搜索工作。查询涉及的内容主要为:显示方式以及语法。查询显示方式可以是通过 Hue、Custom UI 或者 Custom App 来实现。

索引的建立是为了能够更好更快的查询相关的数据,在查询的时候,我们可以通过多种方式进行数据的查询工作,比如通过 shell 语句进行查询,比如通过相关的 UI 界面进行查询等,查询又分为了多种形式的查询,比如全文查询(从当前所有的数据中进行遍历式的查询操作)、关键字查询以及条件筛选查询等。

 2.Solr 总述

在 Solr 中,我们主要关注上图中的几个组件:

(1)ConfigSet:是 Solr 的工作配置文件,该文件指导了 Solr 的工作方式,索引以及相关对应的数据文件的存储路径,和在实际工作中的一些参数值(2)SolrCloud:SolrCloud 相当于是进程一端的最高进程,通过 SolrCloud,

我们可以创建 Solr 实例,Solr 的 Core 核心,也就是指 SolrCloud 是 Solr中的总进程,我们通过 Solr 的总进程可以控制 Solr 实例的创建、删除、故障的切换等一系列的关于进程的执行操作,SolrCloud 具有 Solr 的最高执行权限。

(3)Solr:Solr 实例就是 SolrCloud 在进行工作时的创建的一个个针对于不同的应用的实际工作进程,所谓说实例,就是指类的一个具体的范畴,比如鸟类就是一个类别,孔雀就是鸟类中的一个实际的例子,所以孔雀就作为了一个鸟类的实例。所以在 Solr 中,我们创建的实际的索引进程都可以称之为是 Solr 中的一个实例。

(4)Core:Core 相当于是一个执行处理引擎,我们发送的数据的查询检索请求实际上都是通过 Core 的执行来进行操作的,Core 获取到用户的请求, 然后根据用户的请求转化为对应的执行动作,然后 Core 会去查询或者检索数据,将执行的结果进行统一的整合,最终发送给请求者。所以实际来说,Core 相当于是 Solr 中的整体处理核心发动机。

在 Solr 中,我们实际上是分为了控制的实际进程和数据的逻辑进程的,那么上面所说的所有进程都属于是实际存在于 Solr 中的维护和控制进程。 SolrCloud 根据 ConfigSet 中的控制文件,来进行实际的工作,创建并且维护 Solr 实例,然后 Solr 实例中,如果是一个单进程对数据进行相关的维护,那么势必会造成压力过大的情况产生,所以在这个时候,我们就需要将实际的处理进程按照分布式的思想进行切分,通过多进程同时维护就可以保障业务的压力被均分到各个节点上,所以我们在 Solr 实例中创建了 Core 进程来进行实际的工作和数据的维护操作。

那么下面我们所说的就是 Solr 中对于数据的切分维护所产生的逻辑数据进程。(1)对于 Solr 来说,首先数据在 Solr 上也是一个整体的完整索引逻辑存在,我们将这个完整的 solr 索引称为 Collection。

(2)Collection 由于是一个完整的逻辑索引整体,就像之前所说的一样,我们需要做的就是引入分布式的概念,将逻辑数据整体切分给不同的设备去进行维护,那么在这里首先我们先将 Collection 切分为第二级的小逻辑概念,也就相当于是将一个完整的逻辑索引切分为很多的分片,我们称之为是 Shard。如果Collection 不够大,那么可能会出现一个 Shard 就是一个 Collection 的情况。

(3)我们一般会将 Collection 切分为 shard,之后 shard 就不会再做对应的切分了,因为我们需要保护数据的安全,不会因为故障而丢失或者是损坏, 那么这里我们就又进一步借鉴了整体 Hadoop 的设计理论了,采用的是数据副本的形式来进行保护,我们会将 Shard 中的数据创建副本,一个副本就称为一个Replica,由于我们对于索引更多的是读取操作,所以为了保证数据读取的高效性,我们将这些副本都使用到了,而不是像 HDFS 中一样数据副本做备份,在 Solr 中所有的 Replica 都是被使用的,他们提供了相关的读取和查询服务,但是现在就出现了一个问题,就是 Replica 中的数据都是相同的,这个情况下假如某一个节点更改了数据,那么这个时候其他节点如果获取不到更改的信息,就会造成节点和节点之间的数据不同步的问题了。为了解决这个问题,我们在 Replica 这个层级添加了一个新的角色叫做 leader,Leader 其实就是一个权限更高的 Replica,实质上和 Replica 没有区别。我们将一个 shard 的多副本数据称之为是一个组,那么一个组内就会存在多个 Replica,这些 Replica 通过选举的方式选出一个 Leader,当收到一个读取请求的时候,由于不会对现有的数据造成什么影响, 所以只读条件下的正常读取是没有问题的, 每一个 Replica 都可以去执行读和查询的操作,但是一旦涉及到写操作,这个时候就会改变全局所有组内 Replica 的数据,这个时候如果某一个 Replica 收到写请求之后,就必须将请求转发给 Leader 去执行。

3.逻辑视图

就像之前说的那样,我们的进程和数据是相互关联的,Core 作为实际进行工作的进程,关联的也是数据层面提供业务的进程,所以这里 Core 是关联到 Replica的。如上图所示,这里 SolrCloud、Solr、SolrCore 是实际进程,Replica 是存储实际的数据,shard 和 Collection 实际上都是以逻辑进程存在的。

4.SolrCloud 倒排索引

传统的搜索方式(正排序索引)是从关键点出发,然后再通过关键点找到关键点代表的信息中能够满足搜索条件的特定信息,即通过 KEY 寻找 VALUE。通过正排序索引进行搜索,就是从通过文档编号找关键词。

Solr(Lucene)的搜索采用了倒排序索引的方式,即通过 VALUE 找 KEY。而在中文全文搜索中 VALUE 就是我们要搜索的关键词,存放所有关键词的地方叫词典。KEY 是文档标号列表(通过文档标号列表我们可以找到出现过要搜索关键词 --VALUE 的文档),具体如下面的图所示:通过倒排索引进行搜索,就是通过关键词查询对应的文档编号,再通过文档编号找文档,类似于查字典,或通过查书目录查指定页码书的内容。

具体的来解析传统索引和倒排索引的问题,我们可以用上面的例子来看,传统的正排索引就类似于是查英文字典,我们需要进行全文的查找,然后匹配我们搜索的 key 值,也就相当于是当我们需要查找一个数据的时候,我们是通过遍历数据的形式去做匹配的,但是倒排索引我们就需要先创建一个字典,通过字典中记录的关键词去查找数据,就像是中文的字典中有目录索引一样。我们通过索引直接找到对应的数据。

分布式索引流程

①当 Client 发起一次文档索引请求时,首先将从 zookeeper 集群中获取

SolrCloud 中 SolrServer 的集群信息,根据请求中的 collection 信息,获取任意一台包含该 collection 信息的 SolrServer;②然后 Client 把文档索引请求发送给 SolrServer 中该 collection 对应 shard中的一个 Replica 进行处理;

③如果该 Replica 不是 LeaderReplica,则该 Replica 会把文档索引请求再转发给和自己相同 shard 中相对应的 LeaderReplica;

④该 LeaderReplica 在本地完成文档的索引后,会再把文档索引请求路由给本Shard 中的其他 Replica 进行处理;

⑤如果该文档索引的目标 shard 并不是本次请求的 Shard,那么该 Shard 的 LeaderReplica 会将文档索引请求再次转发给目标 Shard 的 LeaderReplica;

⑥目标 Shard 的 LeaderReplica 在本地完成文档的索引后,会再把文档索引请求再次路由给本 Shard 的其他 Replica 进行处理。

这里的索引流程中,多 Replica 是为了保证 Solr 服务的可靠性,当有 replica 挂掉时,一个 shard 上还有其他 replica 可以工作。但是 replica 复本数增多时候,其索引阶段需要从 LeaderReplica 同步到其他 Replica 的过程增多,索引性能便会下降。建议一般在生产环境,Replica 设置为 2,即每个 Shard 上有两个Replica。每个索引进入到相应的 Shard 上时,都需要经过 Leader 进行处理,这样是为了保证 Shard 上数据的一致性,由 Leader 将请求转发给同一 Shard 上的其他 Replica。

分布式搜索流程

SolrCloud 分布式搜索流程

①当 Client 发起一次搜索请求时;Client 首先将通过 zookeeper 会获取到 SolrServer 服务器集群信息,并随机选取一个含有该 collection 的SolrServer;②然后 Client 把搜索请求发送到该 Collection 在 SolrServer 上对应Shard 中的任意一个 Replica(可以不为 Leader Replica)进行处理;

③该 Replica 再根据查询索引的方式,启动分布式查询,基于 Collection 的 Shard 个数(在上图中为 2 个-Shard1 和 Shard2),把查询转换为多个子查询,并把每个子查询分发到对应 Shard 的任意一个 Replica(可以不为 Leader Replica)中进行处理;

④每个子查询完成查询操作后,并查询结果返回;

⑤首次收到查询请求的 Replica 收到各个子查询的查询结果后,对各个查询结果进行合并处理,然后把最终的查询结果返回给 Client。在查询时候,请求是随意发到一个 replica,并非需要 leader 进行处理,这样当 replica 数据增多时,并发查询下,Solr 的查询性能加快。

5.Solr 索引 HBase 数据

Solr 索引 HBase 数据是将 HBase 数据写到 HDFS 的同时,Solr 建立相应的 HBase索引数据。其中索引 id 与 HBase 数据的 rowkey 对应,保证每条索引数据与 HBase 数据的唯一,实现 HBase 数据全文检索。

HBaseIndexer 是 Solr 索引 HBase 数据的工具,主要功能有批量索引和实时索引批量索引通过创建 MR 任务,将导入 HBase 内的数据,在 Solr 中建立索引。批量索引可以基于 scan 方式将全表数据全部扫描一遍,再在 Solr 中建立索引;也可使用 Loader 增量导入 HBase 表的过程中生成的 rowkey 列表,再提交 MR 任务进行增量索引;实时索引则是基于 HBase 的 replication 机制,在数据导入 HBase 的同时,在 Solr 内建立索引,但是效率相比批量索引,增量索引会差一些。

批量索引:

在 HBase 表中在已有大量数据的情况下,可以使用 HBaseIndexer 提交 MR 任务进行批量索引。

(1)读取 HBase 表获取 Region 信息,划分 Split

(2)扫描每个 split 对应的表数据,每个 reduce 任务生成本地索引文件(3)将索引文件 merge 到 Solr

实时索引:

 

对 HBase 数据实时索引,实时录入 HBase 数据的同时,使用 SolrjAPI 建立索引。

(1) 业务应用程序(sparkstreaming、storm 任务等),从 Kafka 消息队列中拉取数据。

(2) 使用 HBaseClientAPI 写入数据到 HBase 表

(3) 使用 SolrjAPI 写入数据到 Solr

6.Solr 索引 HDFS 数据

SolroverHDFS 主要利用 MapReduce 框架读取 HDFS 上的各种非结构化数据(doc、xml、txt、csv 等),在 SolrCloud 中创建索引文件,同时将索引文件存储在 HDFS 上。

特点:对于大量非结构化数据的全文检索,创建索引速度,处理数据量大,索引存储量大。

在 HDFS 上已有大量数据情况下,可以使用 HDFSIndexer 来批量索引。HDFSIndexer通过创建 MR 任务,扫描 HDFS 上指定目录下的结构化文件,采用 Merge index 文件方法向 Solr 建立索引。

7.参考文献

小时候我们都使用过新华字典,妈妈叫你翻开第 38 页,找到“坑爹”所在的位置,此时你会怎么查呢?毫无疑问,你的眼睛会从 38 页的第一个字开始从头至尾地扫描,直到找到“坑爹”二字为止。这种搜索方法叫做顺序扫描法。对于少量的数据,使用顺序扫描是够用的。但是妈妈叫你查出坑爹的“坑”字在哪一页时,你要是从第一页的第一个字逐个的扫描下去,那你真的是被坑了。此时你就需要用到索引。索引记录了“坑”字在哪一页,你只需在索引中找到“坑”字,然后找到对应的页码,答案就出来了。因为在索引中查找“坑”字是非常快的,因为你知道它的偏旁,因此也就可迅速定位到这个字。

那么新华字典的目录(索引表)是怎么编写而成的呢?首先对于新华字典这本书来说,除去目录后,这本书就是一堆没有结构的数据集。但是聪明的人类善于思考总结,发现每个字都会对应到一个页码,比如“坑”字就在第 38 页,“爹”字在第 90 页。于是他们就从中提取这些信息,构造成一个有结构的数据。类似数据库中的表结构:

wordpage_no

--------------

无    38

语    90

......

这样就形成了一个完整的目录(索引库),查找的时候就非常方便了。对于全文检索也是类似的原理,它可以归结为两个过程:1.索引创建(Indexing)2.搜索索引(Search)。那么索引到底是如何创建的呢?索引里面存放的又是什么东西呢?搜索的的时候又是如何去查找索引的呢?带着这一系列问题继续往下看。

索引

Solr/Lucene 采用的是一种反向索引,所谓反向索引:就是从关键字到文档的映射过程,保存这种映射这种信息的索引称为反向索引

  • 左边保存的是字符串序列
  • 右边是字符串的文档(Document)编号链表,称为倒排表(PostingList)字段串列表和文档编号链表两者构成了一个字典。现在想搜索”lucene”,那么索引直接告诉我们,包含有”lucene”的文档有:2,3,10,35,92,而无需在整个文档库中逐个查找。如果是想搜既包含”lucene”又包含”solr”的文档,那么与之对应的两个倒排表去交集即可获得:3、10、35、92。

索引创建假设有如下两个原始文档:

文档一:Students should be allowed to go out with their friends, but not allowed to drink beer.

文档二:My friend Jerry went to school to see his students but found them drunk which is not allowed.

创建过程大概分为如下步骤:

一:把原始文档交给分词组件(Tokenizer)

分词组件(Tokenizer)会做以下几件事情(这个过程称为:Tokenize),处理得到的结果是词汇单元(Token)

 

  1. 将文档分成一个一个单独的单词
  2. 去除标点符号
  3. 去除停词(stopword)

所谓停词(Stopword)就是一种语言中没有具体含义,因而大多数情况下不会作为搜索的关键词,这样一来创建索引时能减少索引的大小。英语中停词(Stopword)如:”the”、”a”、”this”,中文有:”的,得”等。不同语种的分词组件

(Tokenizer),都有自己的停词(stopword)集合。经过分词(Tokenizer)后得到的结果称为词汇单元(Token)。上例子中,便得到以下词汇单元(Token):"Students","allowed","go","their","friends","allowed","drink","beer","My","friend","Jerry","went","school","see","his","students","found","them","drunk","allowed"

二:词汇单元(Token)传给语言处理组件(LinguisticProcessor)

语言处理组件(linguisticprocessor)主要是对得到的词元(Token)做一些语言相关的处理。对于英语,语言处理组件(LinguisticProcessor)一般做以下几点:

  1. 变为小写(Lowercase)。
  2. 将单词缩减为词根形式,如”cars”到”car”等。这种操作称为:stemming。
  3. 将单词转变为词根形式,如”drove”到”drive”等。这种操作称为:lemmatization。

语言处理组件(linguisticprocessor)处理得到的结果称为词(Term),例子中经过语言处理后得到的词(Term)如下:

"student","allow","go","their","friend","allow","drink","beer","my","friend","jerry","go","school","see","his","student","find",

"them","drink","allow"。

经过语言处理后,搜索 drive 时 drove 也能被搜索出来。 Stemming 和 lemmatization 的异同:

相同之处:

Stemming 和 lemmatization 都要使词汇成为词根形式。不同之处:两者的方式不同:

(1) Stemming 采用的是”缩减”的方式:”cars”到”car”,”driving”到”drive”。

(2) Lemmatization     采 用 的 是  ”  转 变  ”  的 方 式 :  ”drove”到”drove”,”driving”到”drive”。

两者的算法不同:

(1) Stemming 主要是采取某种固定的算法来做这种缩减,如去除”s”,去

除 ”ing” 加 ”e” ,将 ”ational” 变为 ”ate” ,将 ”tional” 变为”tion”

(2) Lemmatization 主要是采用事先约定的格式保存某种字典中。比如字典中有”driving”到”drive”,”drove”到”drive”,”am,is,are”

到”be”的映射,做转变时,按照字典中约定的方式转换就可以了。

(3) Stemming 和 lemmatization 不是互斥关系,是有交集的,有的词利用这两种方式都能达到相同的转换。

三:得到的词(Term)传递给索引组件(Indexer)

(1)利用得到的词(Term)创建一个字典 Term     DocumentID

student

1

allow

1

go

1

 

their

1

friend

1

allow

1

drink

1

beer

1

my

2

 

friend

2

jerry

2

go

2

 

school

2

see 2

 

his 2

 

student

2

find

2

them

2

drink

2

allow

2

(2)对字典按字母顺序排序:

 

Term

Document ID allow

1

   

 

allow

1

allow

2

beer

1

drink

1

drink

2

find

2

friend

1

friend

2

go

1

 

go

2

 

his 2

 

jerry

2

my

2

 

school 2

see 2

 

student

1

student

2

their

1

them

2

3.合并相同的词(Term)成为文档倒排(Posting List)链表

Document Frequency:文档频次,表示多少文档出现过此词(Term) Frequency:词频,表示某个文档中该词(Term)出现过几次对词(Term)“allow”来讲,总共有两篇文档包含此词(Term),词(Term)后面的文档链表总共有两个,第一个表示包含”allow”的第一篇文档,即 1 号文档,此文档中,”allow”出现了 2 次,第二个表示包含”allow”的第二个文档,是2 号文档,此文档中,”allow”出现了 1 次至此索引创建完成,搜索”drive”时,”driving”,”drove”,”driven”也能够被搜到。因为在索引中,”driving”,”drove”,”driven”都会经过语言处理而变成”drive”,在搜索时,如果您输入”driving”,输入的查询语句同样经过分词组件和语言处理组件处理的步骤,变为查询”drive”,从而可以搜索到想要的文档。搜索步骤搜索”microsoftjob”,用户的目的是希望在微软找一份工作,如果搜出来的结果是:”Microsoft does a good job at software industry…”,这就与用户的期望偏离太远了。如何进行合理有效的搜索,搜索出用户最想要得结果呢?搜索主要有如下步骤:

一:对查询内容进行词法分析、语法分析、语言处理

  1. 词法分析:区分查询内容中单词和关键字,比如: englishand ,janpan,”and”就是关键字,”english”和”janpan”是普通单词。
  2. 根据查询语法的语法规则形成一棵树
  3. 语言处理,和创建索引时处理方式是一样的。比如:leaned–>lean, driven–>drive

 

二:搜索索引,得到符合语法树的文档集合三:根据查询语句与文档的相关性,对结果进行排序

我们把查询语句也看作是一个文档,对文档与文档之间的相关性(relevance)进行打分(scoring),分数高比较越相关,排名就越靠前。当然还可以人工影响打分,比如百度搜索,就不一定完全按照相关性来排名的。

如何评判文档之间的相关性?一个文档由多个(或者一个)词(Term)组成,比如:”solr”, “toturial”,不同的词可能重要性不一样,比如 solr 就比 toturial 重要,如果一个文档出现了 10 次 toturial,但只出现了一次 solr,而另一文档 solr 出现了 4 次,toturial 出现一次,那么后者很有可能就是我们想要的搜的结果。这就引申出权重(Term weight)的概念。权重表示该词在文档中的重要程度,越重要的词当然权重越高,因此在计算文档相关性时影响力就更大。通过词之间的权重得到文档相关性的过程叫做空间向量模型算法(Vector Space Model)

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值