Java面试题2.0--solr

欢迎关注《Java面试题2.0》合集发布页,持续更新中!

 
 
概念:
 
Solr是目前非常受欢迎的基于Apache开源组织下Lucene开发的一个开源高性能的企业级搜索平台。Solr具有高度可靠性、可扩展性、可容错性的特点,提供了分布式索引、索引备份、查询负载均衡、自动故障转移和恢复,以及集中配置等功能。
 
core
 
想要在Solr中添加索引,你需要指定一个Core,即你需要把索引数据添加
 
Solr中的Core术语指的是一个单一的索引数据,而索引又是由多个Document组成的,所以你把Core理解为多个Document打包成的一个集合。需要注意的是,Solr里的Document是扁平化的,即两个Document的域可以完全不同,也就是说表示客户信息的Document和表示产品信息的Dcoument这两个完全不同结构的数据可以放到一个Core里
 
Solr设计多Core来存放索引就是为了实现把两个不相干的对象进行分离独立管理,这样每个对象的索引都有自己的一套Schema.xml、solrconfig.xml进行管理,彼此之间互不影响。比如有客户和订单两个对象,你可以将他们分成两个Core存储,这样能实现单独对其中任意一个Core进行数据更新或重新加载等的操作,为他们配置不同的域及其他配置参数,而互不干扰且方便维护。其次,分成多个Core可以减少单个Core的索引体积大小,这样也影响了你配置Solr.
 
两个数据是不是要分成两个core分开存放?
 
要综合考虑。比如你有课程表和分数表数据,你需要从以下几个方面来考虑:
1)你的系统是否明确划分为课程和分数管理两个模块。
2)是否有这样一个场景:你需要返回课程和分数两个类型的全部数据?因为跨Core Join查询会损耗性能,这部分损耗你是否可以接受?
3)每个类型索引数据的更新频率一致吗?如果两者其中一个几乎不更新,而另一个经常更新,更新频率不一致,自然不适合混合在一起存为单个Core。
4)两者数据是否明确做权限划分,即可能希望课程表数据对于A应用可见,而对于B应用不可见,那你最好是分Core存放。
5)分成多个Core来存放,带来索引数据维护的复杂度,但分成单Core带来索引体积增大,单次索引重建耗时会加长。
 
为什么要设计多Core?
 
·重建索引;
·配置变更影响最小化;
·索引合并和分裂;
·Core热交换。
 
solr与关系型数据库的区别:
 
数据库里有简单的基于通配符的文本模糊查询,但这会导致全表扫描,性能很差,而Solr是把搜索关键字保存在一个倒排表里,搜索性能提高了N个数量级。但Solr创建索引速度相对较慢。
 
Solr里更新部分字段(域)数据相对较慢,因为Solr里更新只能先删除再新增。而且在新增数据的可见性方面,数据库能立马可见,Solr近实时查询的数据可见性则稍差些。
 
Solr的魅力在于它灵活的Schema机制,由于Schema.xml约束比较宽松,你甚至可以认为Solr的Schema.xml只是个摆设,每个Document可以有任意个任意类型的域,而数据库里的表的字段是提前限定的,且每一行记录拥有的字段数必须一致。
 
配置文件
 
schema.xml:主要用来定义你索引数据需要的域和域类型,即你需要在这里声明索引数据中需要哪些域以及每个域的类型(域类型决定了被该类型修饰的域的域值该如何被索引、如何被分词、如何被存储等)
 
solrconfig.xml:主要用来配置索引创建、查询、Solr缓存以及Solr组件处理器等信息。
 
以stopwords.txt:自定义的停用词字典文件
 
synonyms.txt:自定义的同义词字典文件
 
data目录:主要用来存放你的索引数据和Solr日志文件
 
DIH
 
大多数的应用程序将数据存储在关系数据库、xml文件中。对这样的数据进行搜索是很常见的应用。DIH(Data Import Handler)提供了一种可配置的方式向Solr中导入数据,可以一次性全量导入,也可以增量导入。
 
通过Solr后台的Documents添加界面去添加索引,由于只能单个Document添加,效率太低。可能你的索引数据全都保存在文本文件里,比如txt文件,那么,如何批量去索引某个文件夹下的文本文件呢?Solr的Web后台其实提供了数据导入功能简称DIH。
 
开始之前,你需要了解DIH能帮你做些什么:
1)它能读取数据库的数据并创建索引;
2)它能够基于配置的方式把数据里表的列甚至多个表的数据聚合并解析成一个Document;
3)它支持基于配置的全量和增量数据导入;
4)它能实现基于配置的定时全量和增量索引;
5)它能基于HTTP方式读取并索引XML文件;
6)它支持各种基于插件式的datasource和formate配置。
 
Tika
 
有时候我们需要索引的数据可能存在于PDF、Word、Excel等文件中,我们需要去解析文件从中获取数据然后建立索引,而在Solr中这类文件解析工作由Tika负责。Tika将诸如PDF、Word、Excel的文件统一抽象为富文本文件,然后定义一套接口去提取它们的内容。使用者可直接调用该接口,而不用考虑文件类型以及不同类型文件的提取过程。
 
全量索引
 
所谓全量索引一般指的是每次从数据库中读取需要导入的全部数据,然后提交到Solr Server,最后删除指定Core的所有索引数据进行重建。全量导入一般在数据首次导入或者备份数据恢复时执行。
 
增量索引
 
当索引数据量很大时,每次都依靠全量导入数据显然很不切实际,所以增量导入索引数据显得格外重要。
当增量导入操作被执行,它会读取存储在conf/deltaimport.properties配置文件,利用配置文件里记录的上一次操作时间来运行增量查询,增量导入完成后,会更新conf/deltaimport.properties配置文件里的上一次操作时间戳。首次执行增量导入时,若conf/deltaimport.properties配置文件不存在,会自动新建。
 
如果要使用增量导入,前提你的表必须有两个字段:一个是删除标志字段即逻辑删除标志:isdeleted,另一个则是数据创建时间字段:create_date,字段名称不一定非得是isdeleted和create_date,但必须要包含两个表示该含义的字段。根据数据创建时间跟上一次增量导入操作时间一比对,就可以通过SQL语句查询出需要增量导入的数据,根据isdeleted字段可以查询出被标记为删除的数据,这些数据的ID主键需要传递给Solr,这样Solr就能同步删除索引中相关Document,实现数据增量更新。如果你数据表里的数据都是物理删除,没有逻辑删除标志位字段的话,那么找出已经被删除的数据显得比较困难,所以这就是需要逻辑删除标志字段的原因。
 
Lucene索引原理名词
 
索引:·在Lucene中一个索引是放在一个文件夹中的,一个索引就是多个Document的集合。
 
文档:·文档是我们构建索引的基本单位,索引中的每个Document就好比数据库表中的每条记录Record,虽然不是一个概念,但你可以这样类比去理解。
 
域(Field):·一个Document其实就是一个Field的集合,每个Field就好比数据库表中的每个字段column。
 
词(Term):·每个Field的域值经过分词器处理后得到的每一项称作Term。
 
 
倒排索引查找数据的整个过程
 
首先需要读取每篇文档的内容,然后找出内容的所有单词,也就是常说的分词处理。对于英文文档,一般就是按照空格进行分词,对于中文而言,则需要使用相应的中文分词器进行特殊处理。得到所有单词后,我们还需要剔除一些毫无意义的单词,比如英文里的“the”“are”“at”“in”“to”“is”,这些词被称为停用词,像中文里的停用词有“的”,“是”,“我”。然后还需要将所有的单词统一转换成小写。由于英文单词还有时态之分,所以我们还需要将还原到单词的原型,比如loved还原成love。最后我们还需要剔除掉其中的所有标点符号,因为也不会有用户会将标点符号作为搜索关键词进行搜索。上面的一系列处理过程全部是由分词器完成,处理完成后,我们将得到如下信息:
 
根据上述信息,我们还只能知道每个文档包含了哪些单词,即文档→词的映射,而倒排索引表是词→文档的映射,即反向信息。由于某个单词可能出现在多个文档中,当用户根据某个单词进行搜索时,可能会返回多个文档给用户,这就涉及到多个文档的排列顺序,我们自然是希望把跟用户输入的搜索关键词相关的文档优先排在前面展示给用户,所以还需要统计每个单词在每个文档中的出现频率即Term Frequency,有时候也需要在返回的结果中将用户的搜索关键词进行高亮显示,所以我们也许需要记录每个单词在文档中的出现位置即Term Position等。而term出现在哪个文档中这些信息是保存在Term Dictionary里,term的出现频率信息是保存在词频文件里,而term的位置信息是保存在位置文件里。统计完这些信息后,我们将得到如下这样一个倒排索引结构:
 
倒排索引表构建完成后,Lucene首先根据用户输入的单词对Term dictionary词典进行二元查找,找到该Term,通过词频文件指针可以读取到该Term对应的文档ID,从而找到该搜索关键词在哪些文档中出现过,最终将结果返回给用户。
 
DocValues:
 
Lucene索引的存储一般都是以倒排索引的方式(term-doc),即Term到Document的一个映射。但是在搜索相关功能处理的时候,如排序、高亮,需要通过文档docid找到相应的term值、term的位置信息等。为此,在Lucene4.0中,引入了一个新字段类型DocValue,即在索引的时候建立文档到值(document-to-value)的映射。这个方法保证减轻了一些字段缓存的内存要求,并且使得Sorting、Faceting、Grouping、Fuction Query的响应速度更快。但开启DocValues需要额外保存索引信息,因此会增大索引体积。
 
DocValues的优缺点:
 
1)近实时索引:在每一个索引段里面都会有一个docvalues数据结构,这个结构与索引同时建立,并且能够快速更新、生效;
3)更好的压缩比:Docvalues fields的压缩效果比fieldcache好,但不强调做到极致;
 
Solr更新
 
第一种方式就是Atomic Update(原子更新)。这种方式允许你只更新索引文档中的一个或多个域,而不需要对整个索引进行重建。原子更新使得每次只更新索引文档的部分数据成为可能。
 
第二种方式就是optimistic concurrency(并发乐观锁),它是很多NoSQL数据库的一个功能特性,它允许根据版本号有条件的更新索引文档,这种方式包含了如何处理版本匹配的语义和规则。原子更新和并发乐观锁既可以独立的管理文档的更新,也可以结合一起使用。但需要注意的是,并发乐观锁只是保证了对于同一个索引文档不会出现两个索引更新操作同时并发执行,但对索引更新操作本身是不是原子更新并不关心。
 
分词:
 
分词,就是将用户输入的一串文本分割成一个个token,然后进行遍历,对其进行过滤的操作,比如去除停用词、特殊字符、标点符号和统一转成小写形式等。分词的准确与否,直接影响搜索结果的相关度排序。
 
keywordTokenizer: 会将整个文本当做一个Token
LetterTokenizer: 会提取出文本的所有连续的字母序列,并去除非字母字符。
LowerCaseTokenizer: 继承自LetterTokenizer,除了拥有LetterTokenizer的功能外,还会降所有的字符转换为小写
ICUTokenizer: 支持对多语言文本进行分词处理
PatternTokenizer: 使用正则表达式来分割
WhitespaceTokenizer: 使用空白字符来分割
StandardTokenizer: 标准分词器,会按照逗号或者连字符进行断词,但不会剔除停用词,不会转换大小写
SentenceTokenizer: 会将输入的文本分解成一个个句子,每个句子作为一个Token,分割费用为逗号、句号和分号等
HMMTokenizer: 支持对中文或中英混合的文本进行分词,但对于中文知识进行简单的单字分割
 
TokenFilter
 
TokenFilter是Tokenizer的过滤器,它用于对Tokenizer处理后的token进行二次过滤。可以不配置,或配置多个。
StopFilter: 会过滤掉在停用词词典里出现的Token
LowerCaseFilter: 将每个token转换成小写形式
LengthFilter: 会过滤掉不符合长度的token
TrimFilter: 用于剔除Token前面和后面的空格字符
ManagedStopFilter: 和StopFilter类似,区别是ManagedStopFilter的停用词数据是从接口获取的
SynonymFilter: 用于Token的同义词映射,需要提前在文件里声明
ManagedSynonymFilter: 从接口获取同义词数据
TypeTokenizer: 可以通过白名单或者黑名单来过滤掉不符合规定的token
keepWordFilter: 用于保护定义在字典文件里的token
RemoveDuplicatestokenFilter: 用于移除重复的token
 
 
CharFilter
 
是一个对输入文本进行预处理的组件。需要配置在Tokenizer之前,可以添加、更新、删除字符,但会保留原始文本的位置偏移量,从而保证对高亮功能的支持
 
中文分词器
 
中文分词器:对中文语句中包含的词汇进行断词。分词器的核心就是算法和字典,算法决定了分词效率,词库不完善则可能导致词语分不出来。
 
IKAnalyzer: 开源的。基于java语言开发的轻量级的中文分词工具包
支持用户字典扩展自定义,支持英文、中文等词汇处理,不支持solr5.x以上,需要手动修改源码
 
Ansj分词器:是一个完全开源的,基于google语义模型+条件随机场景模型的中文分词的java实现。
使用简单开箱即用,实现了姓名识别、自定义词典、中文分词、自动摘要等功能
完全独立于Lucene,可以应用到自然语言处理等方面
 
MMSeg4j分词器:是基于MMSeg算法实现的Java中文分词器
 
Paoding分词器:具有高扩展性,能够非常方便的扩充字典
完全采用面向对象设计,拥有极高的分词效率和查字典查找算法,但目前不更新了。
 
Jcseg分词器:集成了关键字提取、关键短语提取以及关键句子提取等
 
Ictclas分词器:为NLPIR,支持中文分词、词性标注、支持多种编码格式等,不是免费的,在java中使用麻烦
 
FuDanNLP: 复旦大学开发的,可以中文分词、信息检索以及机器学习
 
HanLP: 由一系列模型与算法组成的Java开源工具包,目标是普及自然语言处理在生产环境中的应用,功能完善、性能高效
架构清晰和可自定义
 
 
Solr查询
 
相关度:
Relevance是用于表示查询响应满足用户查询需求的一个程度。查询响应的相关性程度取决于该查询的执行场景。
正确率:P = 返回结果中相关文档个数/返回结果的数目
精确率:A = 判断结果正确的文档数目/所有文档数目
召回率:R = 返回结果中相关文档数据/所有相关文档数目
 
通用查询参数:
 
q: 根据q参数构建一个查询,必须指定,必须符合语法规则
q.op: 用于指定boolean操作符,可选值有AND/OR,默认为OR
df: 用于指定查询的默认域
 
defType: 指定Query Paser的类型,默认用标准查询器,即defType=lucene
 
sort: 指定响应结果集的排序规则
用ASC DESC(不区分大小写)来指定排序规则
score asc,rank desc
 
start: 返回结果的偏移量,默认为0
 
rows: 指定一次查询返回多少条数据,默认是10
 
fq: filter query的缩写,表示对查询结果集再发起一次过滤查询
并不进行文档评分操作,所以对于提升复杂查询的性能非常有用。
它拥有独立主查询的缓存,如果使用了相同的query,那么会直接命中缓存
fq参数可以在一个query被指定多次: fq=sales:[100 TO *]&fq=section:0
在Solr查询请求URL中指定的所有参数涉及的特殊字符都应该进行转义或编码成十六进制
 
fl: 返回的结果中包含哪些域,也就是返回哪些字段,可以指定多个,用逗号或空格分隔
 
wt: 用于指定使用什么类型的格式来格式化查询结果,默认json
 
cache: Solr默认会对每个Query以及Filter Query返回放入结果集进行缓存。如果想要禁用缓存,那么你可以设置
cache = false
你也可以设置cost选项来控制不缓存的Filter Query的执行顺序。cost值越大表示它的执行开销越昂贵。通过cost选项你可以控制低开销的Filter Query先于高开销的Filter Query执行。
对于开销很高的Filter Query,如果cache=false且cost>=100且query实现了PostFilter接口,那么结果收集器等主查询和其他Fikter Query全部执行完成后再执行该Filter Query.
fq = {!frange l=10 u=100 cache=false cost=100}mul(sales,price)
 
查询语法:
 
title为alice 且 text 包含go
title: "alice" AND text: go
 
Lucene支持对单个Term进行通配符查询,对单个字符进行模糊用“?”,对多个字符进行模糊用“*”
te?t  t*t
但是不能将通配符放在表达式的开头,这样性能会很差
 
使用~会返回拼写相似的结果,后面可以加上相似度
比如要查询和roam相似的结果: roam~0.8
 
范围查询
使用中括号【】表示包含边界,使用花括号{}表示排除边界。
默认Lucene中不支持Date域,所以再Lucene中日期时间类型的数据只能使用字符串或者转成Long类型的毫秒数,然后
再使用范围查询。
mod_date: [20020101 TO 20030101]
 
Boolean操作符:
AND + OR NOT -
OR: 或的关系,有一个满足即可
AND: 且的关系,必须同时满足
+:用于比配必须包含指定的Term,必须指定在term前面:
必须包含Jack,可能包含rose : +Jack rose
 
-:用于排除指定term的数据   -title:Jack
NOT: 用于排除指定term的数据  title: -Jack
 
Query VS Fliter Query(q VS fq)
 
需要比较两者的区别.Solr查询由两个重要的查询操作组成,他们匹配用户查询操作请求参数以及对匹配的结果集进行排序,以便于匹配度较高的前几个索引文档会被返回。默认索引文档会基于相关性评分进行排序,这意味着在查询结果集被查询并收集到之后,需要一个额外的操作来计算每个匹配的索引文档的相关性评分。
 
fq只有一项单一的职责:对匹配的索引文档进行过滤限制,不会对索引文档进行相关性评分操作。
 
q参数有两项职责:
1、根据用户传入的查询条件匹配符合条件的索引文档;
2、使用相关性算法根据term列表对匹配到的索引文档进行相关性评分。这些term列表可能是用户传入的,也有可能是对用户输入的查询文本字符串经过分词器处理后得到的。
 
q参数作为一个特殊的过滤,会告诉Solr在计算相关性评分时什么Term应该考虑进去。正是因为这种差别,人们更倾向于将输入的查询关键字传递给q参数,然后通过fq参数自动生成filter Query.
 
从q参数构造的主查询中分离出来的Filter Query,会经常在查询之间被重用,因为会在Filter缓存区缓存Filter Query匹配到的索引文档。由于q参数构造的主查询需要对匹配到的每个索引文档进行相关性频分,而将查询的某些部分分离到Filter Query 中,那么被分离到Filter Query的部分匹配到的索引文档将不会进行相关性评分操作,这将大大减少了索引文档的相关性评分的计算工作量。
 
鉴于Filter Query内部的这种工作机制,应该将那些能够过滤掉大部分无效索引文档的查询条件通过fq参数实现,也可以将那些用户使用频率比较高的查询条件使用fq参数来实现,充分利用缓存来提升查询效率
 
fq与q的执行顺序:在filter缓存开启的情况下,fq应该是先于q执行的,先获取缓存数据是否命中。随后q和fq在索引文档收集阶段是并行执行党的,当两者的索引文档收集工作执行完毕后,POST Filter开始执行
 
Post Filter
 
当cost参数值>=100时,此时该Filter Query被称为Post Filter。当q和fq并行收集文档结束后,post filter才会执行。这个特性允许执行开销比较低的fq先执行去限制总的匹配文档数目。随后执行的开销比较昂贵的post filter 会基于一个比较小的额索引文档集合进行过滤查询,这在一定程度上也能提升性能。
 
Fact
 
允许你运行一个查询,而后基于索引文档的某个维度或方面对查询匹配的索引文档进行高标准的分类。它允许你选择一个F
ilter对查询结果集进行过滤。
 
Solr高亮
 
根据用户输入的搜索关键字以及描述域在查询时动态计算出来的一段文本摘要。高亮摘要是动态计算出来的,所以它并不是一开始就存在的,而且它对于每个索引文档来说也不是固定不变的,会跟随用户输入的搜索关键字的不同发生动态变化。
 
spell-check
 
拼写建议
 
AutoSuggest
 
自动建议
 
 
group
 
Solr对查询结果进行分组显示可以采用facet和group方式,但是Facet仅仅只返回每个维度下的统计数字,而Solr中的group除了会统计每个分组的索引文档数,还会返回每个分组下匹配的索引文档。另外一个不通电就是Group可以基于指定的sort参数对分组中的索引文档进行排序。
 
Result grouping VS Field collapsing
 
Field collapsing用于解决索引文档中大部分域的域值相同,只有个别域的域值不同的情况下只返回一个索引文档,比如同一品牌同一类型的衣服,只是颜色和尺码不同,那么就只返回一件商品。而Result grouping通常表示更常用的结果集分组功能。
 
group=true
group.field = product
group.limit=1
 
如果group.limit参数设置为1,可以实现对每个分组下重复文档进行删除
 
使用group.main=true可以合并每个分组的结果形成一个扁平化的列表。
可以对多个域进行分组:group.field = product&group.field = format
rows参数是用来控制有多少个分组返回,而不是有多少索引文档返回。
start和rows控制的是每个分组,而group.limit则是用来设置每个分组内返回多少个文档
 
按照Function动态计算值分组
 
solr还支持其他两种分组方式。第一种有点类似于按照指定域进行分组,但是它允许指定域应用Function Query动态计算值,最后按照动态计算值进行分组。第二种就是按照Query进行分组,它允许同时执行多个Query并返回独立的结果集
 
group.func=map(map(map(popularity,1,5,1),6,10,2),11,100,3)
表示的是popularity的值如果实在【1,5】这个区间的话就落入第一组,如果是在【6,10】这个区间的话就落入第2组,以此类推。Function是可以嵌套的。
 
还可以动态的对任意查询进行分组。可以定义多个Query,同时对多个Query进行分组:比如一个查询匹配所有type=movie的产品,一个查询所有包含“games”关键字的,一个查询所有包含“the games”这个短语的。
 
group=true&
group.query=type:movie&
group.query=game&
group.query="the games"
 
对于任意的分组查询,不管是group.field或group.func还是group.query,你都可以返回多个分组;你可以在原查询基础之上执行多个分组子查询。按照某个域分组,那么某个索引文档必定只可能属于一个分组,但是如果按照多个Query分组,那么某个索引文档可能属于多个分组。
 
group VS facet
 
默认facet查询统计是基于q参数的查询结果集的,而不是group分组查询返回的结果集,这个意味着不管你是否开启分组查询,Facet查询统计返回的结果都是一样的。
 
SolrCloud
 
SolrCloud是设计用来处理跨多台服务器的分布式索引和查询工具,具有高可用性、可扩展性、自动容错性的特点。在SolrCloud中,索引数据被分成多个Shard(分片),而每个Shard可以托管在多台机器上,同时为每个Shard提供副本冗余来提供可扩展性和自动容错性。SolrCloud利用Zookeeper来管理集群中所有节点以及集中管理集群配置文件。
 
利用分片原理,每个collection可以分成若干分片,分别存储在不同的服务器上,同时每个分片又要复制成若干的分片作为备份
 
SolrCloud的核心概念
 
1.逻辑概念一个Solr集群上可以承载多个Collection的索引文档。一个Collection由多个索引文档组成,而一个Collection可以被分割成多个Shard(分片),每个分片中包含了这个Collection中的部分索引文档。理论上讲,一个Collection包含Shard的个数决定了Collection中能够合理包含的索引文档总个数以及单个查询请求可能的并行度。
 
2.物理概念一个Solr集群由一个或多个Solr节点组成,每个Solr节点运行着一个Solr Server实例。每个Solr节点可以包含多个Core,而集群中的每个Core其实就是某个逻辑概念上的分片对应的某一个物理副本。每个副本使用同一套配置文件,每个Shard的副本个数决定了Collection的冗余级别、集群的自动容错度以及高负载情况下的并发查询请求数量。
 
SolrCloud中的Shard
 
当单个节点上的Collection包含的索引文档过大时,你可以通过创建多个Shard进行分别存储。一个Shard是一个Collection的逻辑部分,它包含了Collection中的部分索引文档,因此Collection中的每个索引文档都直接属于一个Shard,每个索引文档分配给哪个Shard则取决于你的Shard分片策略。
 
分片选举策略
 
SolrCloud支持自动的分布式索引和查询,同时借助Zookeeper提供了自动故障转移和负载均衡功能。此外,每个Shard同时还可以拥有多个Replica以提供额外的系统健壮性。在SolrCloud模式下,没有Master和Slave的概念,取而代之的是,每个Shard由至少一个物理存在的Replica组成,其中一个Replica会被选举为Leader。如果一个Leader挂掉了,Zookeeper会自动以其他Replica节点中选举新的Leader,从而保证集群高可用。当你发送一个索引文档到Solr集群的任意一个节点请求创建索引时,Solr集群首先判断索引文档属于哪个Shard,然后确定该Shard的Leader所在节点,紧接着索引文档会转发到当前Leader节点上进行索引,最后Leader节点会将索引文档更新广播给其他Replica。
 
 
各个概念术语之间的区别与联系:
 
Collection:同一类型的索引文档的集合,但是这些索引文档并不实际存储在同一台机器上,它们通常会被分割成多个分片,然后每个分片会创建多个副本,所以实际存储的是副本,同一个Shard下的每个副本会分散到多个节点上存储。
 
Shard:单个Collection的逻辑划分,划分出来的每一份称之为Shard,这里的划分只是逻辑概念上的分割。
 
Replica:通过逻辑划分出来的Shard会以多个副本的形式实际物理存储在多个节点上,每个副本其实可以看作一个物理存在的“Core”,只不过此时副本应用的schema.xml和solrconfig.xml是托管在Zookeeper上的。
 
Leader:每个Shard会复制出多个副本,其中一个副本会被选举为Leader(领导),由Leader来负责主导分布式环境下的索引和查询请求,与Leader对应的还有Follwer(表示追随者)。
 
Core:物理存在于节点硬盘上的多个索引文档集合以及这些索引文档相关的配置文件共同组成一个Solr Core。这里的重点是Core中的索引文档都是物理存储在同一个节点的同一个索引目录下,而Collection下的索引文档是以多个Shard下的副本分散物理存储在多个节点的索引目录下,一个副本只会物理存储于一个节点上,但同一个Shard下的副本必定物理存储于不同的节点上。
 
Node:表示一个Solr Server实例,而一个Solr Server实例通常运行于Web容器,由于Web容器可以提供不同的端口号从而启动不同的JVM实例,这意味着同一台服务器上可以部署多个Solr Server实例。一个Solr Server实例可以拥有多个Solr Core,而每个Core下可以包含某个Collection的部分索引文档,此时这里的每个Core其实就是该Collection某个Shard下的Replica(副本)。
 
Cluster:表示一个Solr集群,所有的Solr Server实例一起托管着所有Solr Core,在集群环境下,每个Solr Server实例下管理的每个Core其实就是Collection下某个Shard的某一个Replica(副本)。
 
Zookeeper
 
ZooKeeper是分布式系统里的协调框架,Solr使用ZooKeeper来实现3个关键操作:
配置文件的集中存储与分发;
探测和通知集群状态更新;❑Shard Leader选举。
 
索引复制
 
Solr里的基于Java实现、通过HTTP协议工作的索引复制功能具有以下几点特性:
Solr的索引主从复制不需要借助外部脚本。
只需要在solrconfig.xml中进行配置。
除了能够复制索引数据同时支持复制配置文件。
能够使用相同的配置跨平台工作。
不依赖于特定操作系统的文件系统特性,比如硬链接。
与Solr紧密集成,Solr提供了一个后台管理界面来细粒度的全方位的控制IndexReplication(索引复制)。
Solr里的索引复制功能被实现为一个Request Handler,配置索引复制理论上与配置Solr里的其他普通Request Handler基本相似。
 
索引复制工作机制
 
对于Master而言,当startup、commit、optimize(索引优化)这其中任意一个操作在Master节点上执行完成之后,即表示Slave节点可以开始主动pull索引文件了。对于Slave而言,Master节点根本不用关心Slave节点,因为Slave节点会不断的轮询(依赖于pollInterval参数)Master节点,检查Master节点上的当前索引文件版本号,如果Slave发现Master上的版本号比自身的新,那么Slave就会启动索引复制操作。
 
对于复制索引文件而言,Slave如果发现本地的索引文件与从Master下载而来的索引文件在文件大小和时间戳方面不相同,意味着Master和Slave之间的索引文件不兼容。为了解决这个问题,Slave会将从Master下载而来的所有索引文件全部复制到一个新的索引目录,然后请求重新加载Core并从新索引目录加载索引。
 
查询性能优化
 
禁用FilterQuery的缓存:fq = {! cache=false}
 
至于什么时候应该禁用FilterQuery的缓存,你应该考虑的是你的Filter Query是否拥有共性,比如fq=category:books就适合使用Filter缓存,因为category域的域值就固定的几个,用户指定的category参数值可能不同,但是毕竟category域值可选值可以预计不会很多,不会达到几百上千个。而且category:books这类查询用户使用频率高,这样缓存命中率就高,查询性能自然得以提升。比如时间区间查询,由于区间范围不确定性,无法确定对哪个区间的查询进行缓存,即便区间确定了,也很难保证每个用户都会频繁命中这个区间。总之,你要谨记缓存确实可以提升查询性能,但是同时你还要考虑缓存命中率,如果设置一个缓存几天几个月都没有命中几次,那么这是对内存和CPU资源的一种浪费行为。
 
假如你的Solr查询中有多个FilterQuery,此时你需要考虑每个Filter Query的执行顺序,因为Filter Query的执行顺序可能会影响最终Solr查询的性能.应该优先让那些能够过滤掉大部分索引文档的FilterQuery先执行,而控制FilterQuery执行顺序的就是通过设置cost属性值,cost属性值越小表明其执行开销越小,应该越优先执行.
 
优化点:
 
前缀查询使用N-Gram来实现,而不是使用abc*的方式进行查询。
Phrase Query(短语查询)可以尝试使用ShingleFilterFactory来提升性能。
你可以使用游标来提升Solr分页性能。
查询的时候fl参数尽量不要返回无关的域,尽量少返回stored=true的域。
尽量减少fq的个数,考虑是否可以将多个fq合并为单个fq查询。
如果你确定不需要Solr中的NRT近实时查询,那么请注释掉solrconfig.xml中的自动软提交配置。
 
如果你正在使用Field Facet,那么此时你可以尝试在请求参数中设置facet.threads=1000来提升Field Facet查询的性能,但是此参数只适用于Field Facet查询,并且当你的Solr查询并发量很大时请不要开启此参数,比如互联网电商项目中请不要使用。
 
在SolrCloud模式下,如果你明确知道你想要查询的Document在哪些Shard上,那么请显式指定shards参数或者_route_参数。
如果有可能,尽量使用最新版本的JDK和Solr。
如果单机已经无法支撑你的业务需求,此时你可以考虑使用SolrCloud来解决单机性能瓶颈。
进一步考虑业务是否可以拆分,比如原来的音乐、电影查询业务是否可以拆分为两个业务。
 
 
 
 
 
 
 
 
 
 
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值