Solr权威指南下卷读书笔记

第11章Solr高级查询
通过第11章,你将可以学习到以下内容:
0掌握如何使用Function Query以及如何自定义Function Query;
0掌握如何使用Geospatial Query; ;
0掌握如何使用Pivot Facet和Subfacet;
0掌握如何使用JSON Facet API来实现复杂的数据统计查询;
0掌握如何使用Solr中的其他查询组件,比如Elevation (竞价排名组件);
0掌握如何使用Solr中的ResultClustering组件实现自动结果集聚类分组。

11.1 Solr 函数查询
Solr中的Function Query (函数查询)允许你为每个索引文档执行一个函数进行动态计
算值。Function Query是- -个比较特殊的查询,函数动态计算后得到的值可以作为一个关键字添加到查询中,也可以作为文档的评分,就像是一-个普通的关键字查询同时还能生成相关性评分。通过使用Function Query,函数动态计算值可以被用于修改索引文档的相关性评分,以及查询结果集排序,而且函数动态计算值还可以作为一一个“伪域”被动态添加到每个匹配的索引文档中并返回给用户。Function Query还支持嵌套,意思就是一个Function的输出可以作为另一个Function的输人,Function 支持任意深度的嵌套。

11.1.1 Function语法
Solr中标准的Function语法是先指定- - 个Function名称,后面紧跟着一对小括号, 小
括号内可以传入零个或多个输人参数,语法使用示例如下:

11.1.2使用函数查询

11.1.4根据函数进行排序

11.1. .5 Solr中的内置函数
到目前为止,你已经知道如何在Solr中应用Function。由于Solr内置的函数非常多,
而且还在不断增加中,所以本书这部分内容不可能面面俱到,如果本书有遗漏某个函数没有
提及,读者可以自行查阅资料学习。但是我会尽量覆盖Solr中内置的大部分常用函数,并详
细解释每个函数的用途以及使用语法。Solr 中的内置函数大致分为5类: data transformation
(数据转换)、Math (数学计算)、Relevancy (计算相关性评分)Distance (距离计算)、Boolean (布
尔操作)。

原书20页

1.数据转换类函数
Solr中比较常用的函数大都是转换类函数,即将数据通过一个或多个函数计算从一一个值
转换成另–个值。下面会详细介绍每个转换类函数的用途和用法(如表11-1所示)。
def(x, y) -如果x 值不存在就返回y,否则就返回x,一般用于设置默认值,防止出现空值
情况

2.数学函数
数学计算是比较常用的数据分析操作。Solr 全面支持数学计算,支持包括加减乘除以及
三角函数等多种数学函数。表11-2列举了Solr中支持的数学函数。

abs(x)-返回x 的绝对值

5.布尔函数
Boolean操作不仅可以用于关键字查询,它还可以用于构建或连接任意复杂的Function
Query (函数查询),通过if、 and、 or、not、 xor、 exists 等函数,可以检查域值或其他函数计算以及基于这些检查有条件的返回域值,表11-5列举了Solr支持的布尔函数。

11.1.6
自定义函数

11.2 Solr 地理空间查询
11.3 Pivot Facet
实现嵌套Facet会很非常有用,这就好比传统关系型数据库中,我们可以groupbya,b对a和b两个字段进行分组统计是类似的。Solr 支持这种嵌套Facet来实现类似SQL里的同时按多个字段group by的功能:Pivot Facet。

Pivot Facet支持多某个域进行Facet。比如你的关于“饭店”的索引数据中包含了state、city、rating这3个域,rating域表示饭店的星级,用1~5颗星星表示。当想要统计各个城市各个州下4~5星级酒店总数时,就可以使用Pivot Facet功能来实现类似这种需求。

11.4 Solr Subfacet
Solr中的Subfacet又称为Nested Facet ( 嵌套Facet)。它是Solr Pivot Facet的升级版,
它允许你为父Facet生成的每个bucket添加额外的子Facet。Subfacet相比PivotFacet有以
下几点优势:
Subfacet使用Facet Function (Facet函数)来进行统计,具有强大的实时分析能力;
可以将一个Subfacet(子Facet)嵌套到任意的Facet类型(比如 Field,Query ,Range)中;
一个Subfacet (子 Facet)可以是任意的Facet类型(比如 Field、Query、Range);
一个给定的Facet可以嵌套多个Subfacet(子Facet);
就像顶级Facet一样,每个Subfacet(子Facet)可以拥有它自己单独的配置(比如offset、limit、 sort、stats等)。
11.5 Solr Facet Function
传统的Facet查询解决了基于某个Facet约束条件来统计查询结果集问题,Solr中扩展传统Facet的 Facet Function支持对Document的域进行聚合操作。将Facet Function与Subfacet功能结合,两者可以提供强大的实时分析统计能力,具体请看后续章节的JSON

11.5.1聚合函数
Subfacet将主体信息划分为多个Facet Bucket,每个Bucket又可以嵌套多个Subfacet,最终会返回每个Bucket的详细信息。Solr提供了Aggregation Function(聚合函数)用于Subfacet的查询统计
11.6 JSON Facet API
采用JSON这种结构特性来实现Subfacet的嵌套比传统查询请求以扁平式结构指定请求
参数显得更加自然且更加灵活。采用JSON Facet API新设计的Facet模块有以下几点目标:
0完美的JSON支持;
0对于复杂的嵌套Facet命令采用JSON格式表示使得程序结构更简单;
0支持更标准化的响应数据输出格式,简化客户端数据解析;
0完美的数据分析功能支持;,
口支持根据任意的计算值对Facet Bucket 进行排序;
口支持以更轻便的方式来执行分布式Facet;
0支持与Solr其他查询功能更好的集成度。
11.6.2 JSON Facet简单使用
新设计的JSONFacet与生俱来就拥有着传统SolrFacet所没有的简单易用的增强型嵌
套式JSON结构语法。以下Solr中的RangeFacet命令分别采用传统Facet和增强型JSON
Facet两种写法:

11.6.4 JSON Facet语法
JSON Facet语法的通用格式如下:出0电城在证
<facet_ name> : { <facet .type> : <facet_ parameter> }
用即你定义的Facet (即维度)名称,名称可 自定义<facet_ type>表示Facet
查询的类型,可选值有terms、query、range。 <facet_ parameter> 表示一些Facet参数,可以有多个,示例如下所示:
top_ authors : { terms : { field : authors, limit : 5 } }
11.7 Interval Facet
Solr 中支持另外一种Facet形式:Interval Facet。它跟Range Facet很相似,但更像是Facet Query,它允许设置变量间隔并统计在指定域的值间隔内的索引文档总数量。尽管这样的功能我们可以通过使用Facet Query 加Range Query实现,但是Interval Facet更适合于为同一个域设置多个值间隔,而Facet Query更适合于充分利用Filter缓存使得查询更高效,Interval Facet默认会使用docValues来保证性能,当然前提是该域的docValues=true,如果该域的doc Values=false,那么就会使用fieldCache(域缓存)。
&facet .interval={ !key=popularity }some_field
&facet .interval .set={ ! key=bad} [0,5]
&facet . interval.set={ ! key=good} [ 5,* ]
&facet=true
11.9 Solr Stats组件
Solr中的Stats组件可以对numeric、string、date域进行简单的数据统计,类似关系型数据库中的min、max、 avg、count、sum等功能。
返回的结果集部分如下所示:
" stats_fields" : {
“termfreq ( 'text ’ , ’ memory ') “:{
“min” : 2.0 ,
" max” : 2.0,
" count " : 3,
“missing” : 0,
" sum” : 6.0,
" sumofSquares" :12.0,
" mean" :2.0,
" stddev " : 0.0} ,
11.12 Solr Query Elevation组件
Solr中Query Elevation组件允许你通过为给定的查询配置特定的Top N结果集,并且不关心Lucene正常的打分机制。这通常也被称为赞助商查询,这就好比百度最赚钱的业务:竞价排名。比如某个企业付费了,我就优先将有关该企业的信息靠前显示,而直接无视底层的打分机制。即人为的决定某些索引文档显示顺序。当你有这方面需求时,你可能会需要使用Query Elevation组件。尽管此组件可以应用于任意Query Parser(查询语法解析器),但是将其与DisMax或者eDisMax结合使用会更有意义。此外,Query Elevation组件还支持分布式查询。
默认Query Elevation查询组件并没有开启,如果想要使用Query Elevation组件,你需要显式的在solrconfig.xml中配置Ouery Elevation组件,配置示例如下所示:
11.14 本章总结
在本章中,我们系统性的学习了Solr中的函数查询以及如何自定义Function,并学习了Solr中强大的地理空间查询,实现类似“离当前位置方圆多少千米以内的酒店”之类的查询。然后学习Solr的 Pivot Facet多维度嵌套查询,由于Solr中的Pivot Facet具有一定的局限性,于是Solr重写了Facet查询模块,设计了全新的JSON Facet API,其中 Subfacet就是采用JSON Facet API实现,而Subfacet强大的数据统计功能又是借助于Facet Function来完成。同时 Subfacet支持任意深度的嵌套,恰好弥补了传统Pivot Facet 的不足。紧接着我们又陆续学习Solr中提供的各种查询组件,比如Terms组件、Term Vector组件、Stats 数据统计组件、Query Elevation 竞价排名组件、Result Clustering 自动聚类分组组件等等。通常本章的学习,你完全可以构造出更复杂的查询来满足项目需求,通过JSON Facet API你能完成大部分的数据查询统计工作,并使用一些可视化组件比如矩状图、饼状图等形式将统计数据形象的展现出来。下一章我们将继续学习Solr的Join查询以及如何进一步提高查询返回的结果集的相关度。
第12章Solr查询进阶篇
掌握如何在Solr 中使用游标实现高效的深度分页查询;
掌握如何在Solr 中实现对查询返回的查询结果集进行自定义排序;
掌握如何使用Solr 中的Join实现跨索引文档跨Core查询;
掌握如何使用相关性权重来提高查询返回结果集的相关性;掌握Solr中的NRT(近实时)查询;
掌握 Solr 中的Real-time Get查询来实时获取最新版本的索引文档;
掌握如何使用Ranking Query对 Top N索引文档重新评分;
掌握如何如何使用Solr 中的 MoreLikeThis组件;
掌握在 Solr 中如何自定义Query Parser。
12.1 Solr深度分页
在Solr中,默认分页查询需要使用start和 rows参数,分页查询的性能调优可以通过启用queryResultCache和根据你实际分页的每页显示大小调整queryResultWindowSize参数来实现。
一般情况下,默认的分页查询结合QueryResultCache(查询结果集缓存)运行不会有什么大问题,但是极端情况下,假如希望查询第100 000页且每页显示10条,意味着Solr提取前100 000 x10 = 1 000 000条数据,并将这1000 000条数据缓存在内存中,然后在内存中对其排序,最后返回最后10条即用户想要的第100 000页数据。首先在内存中缓存1 000 000条数据需要占用很多内存,并且在内存中对1 000 000条数据进行排序也很耗CPU,因此,默认的分页方式只适合于查询Top N页数据,翻页越到后面,分页查询性能越差。当然对于大部分使用场景来说,用户也不太可能那么无聊的翻到第100页,但是不排除用户可以直接修改请求URL直接跳到第100页恶意的攻击你的搜索服务器。

为此Solr提供了一种全新的分页方式,提出了"Cursor"游标的概念来解决上述默认分页查询的性能问题。Solr中的游标是一个逻辑概念,它不会在服务器上存储任何信息,而是返回一个下一页数据起始位置"Mark"标记值给用户,该标记表示着当前分页起始位置在查询匹配的整个索引结果集中的绝对索引位置。简而言之,游标中包含了分页查询结果集的偏移量。因此,Solr不再需要每次从头开始遍历结果直到我们想要的记录。用户每次分页提供当前Mark标记值即可。游标的设计可以大幅提升深翻页的性能,但是是以消耗内存为代价的
12.3 Solr Join查询
为两张表建立索引一般是建议将字段冗余到一个表中建立一个core

Solr中分单corejoin和跨corejoin
单core的join
相当于子查询

Solr 中的单 Join 查询语法类似如下示例:
/select ?
fl=RETURN_FIELD_1,RETURN_FIELD_2&
q= { !join from=FROM_FIELD to=TO_FIELD ]CONSTRAINT_FIELD:CONSTRAINT_VALUE而SQL里的嵌套子查询语法类似如下示例:
Select RETURN_FIELD_1,RETURN_FIELD_2 FROM join_data
WHERE TO_FIELDIN(
SELECT FROM__FIELD from join_data
WHERE CONSTRAINT_FIELD=’ CONSTRAINT__VALUE’

此处Join并不是真正意义上的Join查询,因为子查询返回的结果集并不能随着主
查询一起,它只能用于限制主查询返回的结果集

Solr要求Join查询涉及的索引文档必须属于同-一个SolrServer,
同时也意味着Solr支持跨Core的Join查询。
12.3.1跨Core Join
Solr中的JoinQueryParser支持fromIndex和toIndex两个参数来启用跨Core的Join查询功能。

我们可以执行下面这个跨Core Join查询
http://localhost : 8080/solr/join_ restaurants/select?
fl=restaurantname, text&q=" Indian"&
fq={!join fromIndex=join_ useractions
toIndex=join_ restaurants from=restaurantid to=id} userid:user123 AND
actiontype:clicked
AND actiondate: [NOW-14DAYS TO *]
fq部分是在"join_ useractions" Core上执行一个子查询,然后利用子查询返回的结果集来约束在"join _restaurants" Core上的q="Indian"主查询。由于有两个查询,因此会返回两个结果集,两者通过一个外键进行Join连接。fromIndex参数表示被连接的Core, toIndex表示主Core, 主Core由请求URL前缀部分的htp://ocalhost:8080/solr/join_restaurants 这里指定的Core决定。from 和to参数用于进行主外键关联,from 参数表示外键。

Solr中的Join查询是通过Join Query Parser 实现的,可以通过指定多个fq参数,并在
多个fq参数上使用Join Query Parser,从而能够实现在单个查询请求中执行多个Join查询。
此外,如果想要为每个Join Query使用Nested Query (嵌套查询),你可以使用Boolean连接符将多个Join Query连接起来。因为在嵌套查询中每个Query Parser独立执行的,就好比主查询中通过AND或者OR连接的两个Term。

除了可以使用Join Query Parser实现Join查询,Solr 中还提供了很多高级Query Parser可
以用于Join查询,比如Block Join Children Query Parser( {!child of-…})以及Block Join Parent
Query Parser( {!parent of-=.})。这两种Join查询实现方式能够使你的Join查询性能更好,但
是这种方式需要你在索引的时候为索引文档定义父子关系。

当你发现使用Join Query Parser实现Join查询性能很差时,可以考虑下采用BlockJoin这种Join查询性能更好的方式来实现。但是需要注意的是,在分布式查询模式下,不能跨多个节点实现跨Core查询。
12.3.2跨Document Join
不仅仅可以跨Core Join,还可以实现跨Document Join。
就是将多个表中字段冗余到一起

创建好"goods" Core并导人测试数据。下面请执行以下的查询示例,如下所示:
//查询商品“iPhone 7”的商品分类
http:llocalhost:8080/solr/goods/select?
q=(!Join from=category_id to=id}goods_name : “iPhone 7”
类似于
SQL: select * from goods where id in
(select category_id from goods where goodsname=‘iPhone 7’)

//查询商品分类"Book"下的所有商品
http://localhost:8080/solr/goods/select?
q= !join fromeid to=category_id}category : "Book "
类似于
SQL: select * from goods where id in(select id from goods where category=‘Book’)

//查询商品分类巴手机“下的名称为"iPhone 7"的商品
http://localhost:8080/solr/goods/select?q=goods_name: “iPhone 7”&fl=*,score&
sort=score desc&fq={ !join from=id to=category _id}category:手机

这里使用了fq参数来进行子查询过滤,因为fq参数构造的Filter Query 会利用Filter缓存,所以查询性能会较高。从上面的查询示例返回的查询结果集来看,你不难发现,跨Document的Join查询每次也只能返回某一种Document,即不能在商品 Document 中返回商品分类Document 的category域,并且不能在商品分类Document中返回商品Document的goods_name域。只能实现类似SQL里的"select * from A where id (select s_id from B where xxx=?)"这样的查询,而不能实现类似SQL里的left Join或者right join。此外跨Document 的 Join查询还有一点不足的地方就是查询返回的索引文档的评分都是相同的。即子查询匹配文档的评分不能用于干预主查询匹配文档的评分。
12.3.3 Block Join
除了上面两种Join方式,Solr中还支持BlockJoin方式实现Join查询,但前提是你的
Document必须是Nested Document (即嵌套Document)。那到底什么是嵌套Document呢?
所谓嵌套Document即你可以在一个Document内部再添加一个Document形成父子关系,
12.3.4 Block Join Facet
12.4深人Solr相关性评分
12.4.1 Field权重
在某些索引文档中,可能某些域的权重你认为比其他域重要。比如你有个跟Product(产品)有关的 Document,那么product_name(产品名称)域可能要比 description(产品描述)域的权重要高,因为用户极有可能会根据产品名称进行搜索。类似的,如果你的索引文档表示的是社交网络的,那么用户名称相比于他的朋友列表域可能权重会较高。
在索引时,你可以在将Document发送给Solr Server之前,对域权重进行设置,比如:


1
Red Lobster
12.4.2Term权重
有时候,你希望为查询中的某个域的个别Term增加权重,它跟查询时为域设置权重的语法类似,但是此时权重值是直接应用在Term身上。以下是几个设置示例:
http://localhost : 8080/solr/your-core/select ?
g=restaurant_name : (red^2 lobster^8)
OR description : (red~2 lobster^8)
12.4.6Document权重
为索引文档增加权重有3种主要方式,其中最简单的方式就是在索引时为索引文档设置权重,这种方式与在索引时为域设置权重类似,
12.5 Solr NRT近实时查询
所谓Near Real Time(简称NRT,翻译过来就是近实时的意思)查询,就是当索引文档被索引后能几乎立即被查询到。当索引提交正在处理进行过程中,索引的更新删除操作并不会被阻塞,也不会等待后台的索引合并工作完成之后才为索引打开一个新的IndexSearcher实例并返回。使用NRT查询,你可以将一个索引提交命令更改为软提交(Soft Commit),软提交能够避免标准提交的巨大代价。有时候可能确实想要使用标准提交来确保索引数据正确无误地写入存储介质中,但是软提交能够让你在索引提交期间就能够近乎实时地看到新添加的索引数据。但是你需要特殊注意缓存和Autowarn(自动预热)的配置,它们会严重影响NRT查询的性能。

一个索引提交操作使得索引更新能够对新的查询请求可见,一个硬提交使用事务日志来获取最新的Document ID,同时对索引文件调用fsync方法确保索引数据写人到硬盘,并且保证即便在断电的极端情况下也不会造成数据丢失。
索引软提交之所以比较快速,是因为它只是使得更新的索引数据能够对查询请求可见,但是并没有刷新硬盘上的索引文件或者在硬盘上创建任何索引文件。如果JVM崩溃了或者服务器断电了,那么上一次硬提交之后的所有索引更新数据将会被丢失。如果你的查询要求索引更新在之后能够快速地被查询到,此时你需要开启索引软提交。软提交可以频繁执行但是硬提交不行。软提交在耗时方面开销较小,但是并不是毫无代价的,因为它会降低你的索引吞吐量。

硬提交(Hard Commit)它会要求所有的段文件必须立即合并为一个段文件,并重写整个索引,这个操作执行开销很大,不能执行太频繁。段文件合并应该合理配置合并策略,并定期执行索引优化,提升查询性能。
为了达到灵活提交目的,Solr为硬提交和软提交设计了自动提交策略,自动提交主要靠maxDocs和maxTime两个参数控制,maxTime参数表示每间隔多少毫秒就触发一次索引提交,maxDocs表示当队列中累积了多少个索引文档就触发一起索引提交。你可以在solrconfig.xml 中配置自动提交策略,配置示例如下所示:

15000</ maxTime>
1000
false</ opensearcher></ autoCommit>
其中 openSearcher表示当执行一次硬提交之后是否立即打开一个新的IndexSearcher实例。同理还有配置,maxTime和 maxDocs参数同样适用于软提交。但是对于软提交配置maxTime更合理,尤其是当索引大量的索引文档时。当批量索引时,如果仅仅只是为了追求索引吞吐量,并不要求索引数据能立即被查询到,那么你可以关闭软提交。通常来说,对于硬提交一般建议是每间隔1-10分钟执行一次,对于软提交一般建议是每间隔Ⅰ秒钟执行一次。通过这样配置,保证了新添加的索引文档能够秒级被查询到,同时假如突然断电了,除非硬提交没来得及写完新更新的索引数据,否则软提交并不会丢失数据。

在Solr中除了可以通过开启软提交来实现近实时查询,还可以通过传递指令的方式实现近实时查询。我们知道,在索引文档时,XML文件中可以通过,元素来指示Solr是添加还是删除索引文档,还可以通过添加元素来指示Solr执行一次硬提交,同理还有用于指示Solr执行一次索引优化。关于这部分内容,在2.2.2章节有做介绍。索引优化其实可以看作一种特殊的索引 Commit操作,它除了需要做索引Coomit之外,还会执行索引合并,所以它的执行开销比硬提交还大。

当你在待索引的XML文件中添加元素,或者直接Http请求发送指令,或者直接通过请求参数显式指示Solr是硬提交还是软提交时,默认是执行硬提交。和元素都支持以下3个参数.这
12.6 Solr Real-time Get查询
除了上一章节中提到的3种方式实现NRT(近实时)查询之外,Solr还提供了Real-time Get API来实现实时GET请求。Real-time Get API允许实时获取最新版本的一个或多个索引文档。这里使用的是Real-time,意思就是真正的实时查询,为的就是区分传统的NRT查询。传统的基于Lucene的近实时查询一直都是通过周期性的刷新一个版本的索引快照的方式来保证近实时查询。而 Real-time Get会保证实时的返回最新版本的索引文档,而且Real-time Get实现实时查询不需要重新打开一个新的IndexSearcher实例。但是,Real-timeGet有个局限性:只能根据Unique-Key(唯一性主键)进行查询。
12.7Solr评分查询
自Solr4.9版本开始,Solr提供了Ranking Query(评分查询)来干预另一个普通Query返回的索引文档的最终评分。你执行一个普通查询(这里简称为A),然后会返回Top N个索引文档,然后可以再为A指定一个更复杂的Ranking Query (这里简称为B),B会返回一个分数,然后A的每个TOP N索引文档的最终评分=A的每个每个TOPN索引文档的原始评分+权重因子B返回的评分。因此B的执行开销会比较大,所以一般将其应用于TOPN结果集上,这样对查询性能影响较小。如果某个索引文档的原始评分很低,即便当使用了B之后,该索引文档的评分会变得很高,但是此时不会对该索引文档进行重新评分。
12.8 Solr MoreLikeThis组件
当你查询返回与你输入关键字相关的索引文档,你可能还希望返回与当前返回的索引文档相似的索引文档,比如你搜索"Java",系统返回了Java相关的技术书籍,同时你可能还希望返回跟 Java类似的书籍,比如"JavaScript"相关书籍。此时你可能需要使用Solr中的MoreLikeThis查询组件。它使用Term在原始的索引文档中查询相似的索引文档。
12.9 Solr 自定义 Query Parser
自定义 Query Parser 需要继承 org.apache. so Ir.search. Q Parser 类,然后 重写
parse()方法 你可以直接从父类继承 qstr localParams solrParams request 等属性信
息,其 qstr 表示查询文本Query Parser 自定义完成之后,还需要自定 QParserPlugin ,用于在 solrconfig.xml中注册 自定义 QParserPlugin 首先需要继承
org.apache.solr.search.QParserPlugin 类, 然后重写其 createParser init 方法, create Parser 方法中创建我们刚刚自定义的 PrefixBoostQParser 类对象即可,这样自定义的 PrefixBoostQParserPlugin 插件就完成了
12.10 本章总结
在本章中,首先我们学习了如何通过深度分页来提供Solr分页查询的性能,但是同时你要谨记Solr深度分页的局限性。然后我们学习了在Solr中如何实现自定义排序来满足特定复杂的需求。接着我们学习了Solr中的Join查询以实现跨Core跨文档的连接查询,但是它并不完全等同于SQL中的join查询,它们各有各的使用场景和限制。紧接着我们通过各种方式来设置权重从而提升查询的相关性。然后我们学习了Solr中的近实时查询和实时GET查询,以及Solr中的MoreLikeThis组件,该组件可返回相似文档推荐给用户,关于用户个性化推荐方面建议使用更专业的Apache Mahout框架来实现,最后我们学习了如何在Solr 中自定义Query Parser。
第13章SolrJ

通过第13章,你将可以学习到以下内容:
什么是 SolrJ;
SolrJ的环境依赖与配置;
SolrClient介绍;
如何使用SolrJ添加更新删除索引;
如何使用SolrJ查询Solr索引数据;
如何使用SolrJ高效导出Solr索引数据;如何使用SolrJ增量更新索引;
如何使用SolrJ原子更新索引;
如何使用SolrJ管理Core和 Schema;
如何使用SolrJ JSON Request API;
如何使用Sparing-Data-Solr。

一般建议SolrJ与 Solr Server主版本保持一致即可。
13.3 SolrClient介绍
SolrJ相关的类都包含在Solr源码的 org.apache.solr.client.solrj包下,该包下面主要包含了5个关键类:SolrClient、SolrRequest、SolrQuery、SolrResponse、ResponseParser。其中SolrClient类表示Solr Client是一个Solr客户端的抽象。通过SolrClient 你可以与Solr Server进行如下操作:

使用SolrJ之前,首先需要构建-一个 SolrClient实例,然后发送一个Solr请求,SolrRequest是Solr请求的抽象,如果需要执行Solr查询,那么你需要构建一个SolrQuery实例,SolrQuery是Solr查询的抽象,最终Solr Server 会返回一个响应信息给客户端,SolrResponse是这里的Solr响应信息的抽象。其中SolrResponse响应信息需要使用ResponseParser 来解析转换成NamedList类型返回给客户端。SolrClient实际被设计为一个抽象类,在使用时,首先需要创建一个HttpSolrClient或者ConcurrentUpdateSolrClient或者LBHttpSolrClient或者CloudSolrClient实例。
13.4 SolrJ简单使用
创建索引文档:
solrInputDocument doc = new solrInputDocument ( ) ;
doc .addField (fieldName , fieldvalue) ;
doc.setField( fieldName , fieldvalue) ;
addField和 setFieldde的区别: addField内部会判断该域是不是多值域,如果是多值域,对同一个域多次调用addField 会追加域值,而setField不管你是什么域,一律覆盖域值。
13.5 SolrJ查询
Solr 中的请求操作统一由SolrRecuest进行抽象,SolrRequest有很多子类实现

所示。其中每个SolrRequest对应Solr中的一种http 请求,每种请求都会对应一个RequestHandler进行接收并处理。我们知道,每个Request Handler都需要提前在solrconfig.xml中通过元素进行注册,每个requestHandler通过一个name属性值一一映射,比如/select对应SearchHandler,lget对应RealTimeGetHandler等,而我们的每个SolrRe-quest实现都对应着一个name属性值,比如QueryRequest实现则对应着/select,即表明将会发送一个 http:/localhost:8080/solr/core1/select?xxx请求,显然 UpdateRequest就是对应着/update,ContentStreamUpdateRequest虽然也对应着/update,
13.7SolrJ增量更新
Solr中内置的增量更新操作其实完全都是在Solr Server端完成,SolrJ客户端只不过是通过Http请求发送了一个指令罢了。实现Solr中的增量更新关键是需要你提前在solrconfig.xml中配置 DataImportHandler

13.9使用SolrJ管理Core
13.10使用SolrJ管理schema.xml
13.11 使用 SolrJ操作JSON Request API
13.12 使用Spring Data Solr
Spring Data Solr是 Spring Data家族的一份子,它基于Spring IOC和SolrJAPI进行了一层薄封装,使得用户可以采用配置或者注解的方式来与Solr Server交互,而且也更容易将Solr集成到现有项目中。

接下来可以采用Java Config的方式来配置Solr Client实例

然后我们需要创建一个Repository接口(可以将Repository 理解为我们开发时常常见到的DAO,仅仅只是换个叫法罢了),继承自Sparing Data Solr中的SolrCrudRepository接口,示例代码如下:
public interface ProductRepository extends SolrCrudRepository<Product , string> ()

13.12.3 Spring Data Solr 中的查询
Spring Data Solr中的 Repository接口方法有2种定义方式,第一种是通过内置的方法名称解析规则自动生成Query,表13-3详细列举了Spring Data Solr中支持的方法名称关键字以及它们底层所表示的Solr查询表达式。

Solr查询表达式
And findByNameAndPopularity q=name:?1 AND popularity:?2

也就是说,只要你的Repository接口方法名称遵循上面的隐式命名规则,那么SpringData Solr 会自动帮你生成相应的Solr查询表达式,而完全不用去实现Repository接口,节省了很多编码量。

除了可以使用Spring Data Solr提供的这种根据方法名称来自动生成Solr Query的策略之外,还可以使用@Query注解来手动指定Solr查询表达式,示例如下:
public interface ProductRepository extends solrRepository<Product,String>(
// 这里的?0表示参数占位符,?0即表示引用接口方法中的第零个参数值,同理还有 ?1,?2
Query ( " inStock : ?0")
List findByAvailable (Boolean available);
@Query注解除了支持普通的Query查询,还支持 Filter Query

@Query注解的value属性表示Solr查询中的q参数,filters属性表示 Solr查询中的fq参数,同理还有fields属性表示fl参数,比如fields = “name”, “id”}即表示当前接口查询返回name和 id两个域。defaultOperator属性表示q.op参数,defType属性表示defType参数,requestHandler属性表示当前接口方法将由Solr Server的哪个Request Handler 来处理,比如 requestHandler=“/select"表示查询,requestHandler=”/update"表示索引添加更新删除。timeAllowed属性用来表示当前接口方法的最大执行超时时间。

除了可以采用将Solr查询表达式通过@Query注解显式的定义在对应的注解属性上之外,还可以将Solr查询表达式定义在外部的properties属性文件中,然后根据name值进行引人,
13.12.4 Spring Data Solr 中的 Repository 详解
Spring Data Solr中的Repository其实就是数据访问层的一个抽象,我们通常称为DAO层,不过在Spring Data家族中叫做Repository.Repository 的接口设计是以领域驱动模式来设计,完全以面向对象的方式来操作Solr。
其中将Solr中的 Document 映射为POJO

如果你认为Spring Data Solr的 Repository接口封装的不够好,可以直接继承Repository接口,自己重新封装。Spring Data Solr中的Repository接口是个象征性的空接口,里面没有定义任何接口方法,因此你可以自定义接口方法
13.12.5 Spring Data Solr 中SolrTemplate 工具类详解
基于SolrJ API又进行了一层薄封装,内部提供的工具方法与SolrClient基本类似。对于简单的查询,

可以通过定义接口方法名称能够自动生成Query或者通过@Query注解方式明确指定查询表达式,但是这两个方式都有不足之处。第一种方式对于复杂查询无能为力,第二种方式虽然能够解决第一种方式的不足之处,注解支持的功能毕竟有限,比如join查询就无法通过注解来实现,而且也不是所有的查询参数都能通过注解以及注解属性来设置。此时,你就需要借助SolrTemplate 工具类以编程的方式来手动实现复杂的Solr请求。

1.SolrTemplate 实例化
你除了可以直接在Spring配置文件中注册SolrTemplate,然后交由 Spring IOC容器帮助我们管理SolrTemplate实例,也可以直接通过SolrTemplate的构造函数手动构造SolrTemplate实例对象。
创建SolrTemplate实例,首先必须提供一个SolrClient对象,这也是创建SolrTemplate实例的最简单方式,如下所示:
solrTemplate template = new SolrTemplate(new HttpSolrclient
( “http:llocalhost:8080/solr” ) );

Spring Data Solr中的SolrDataQuery类型。SolrDataQuery是Spring Data Solr中的查询抽象,而查询必须要有查询条件,Spring Data Solr中的查询条件使用Criteria类进行表示。

然后你就可以将Criteria对象传入SimpleQuery类的构造函数,创建一个SimpleQuery对象(SimpleQuery是SolrDataQuery的一个子类),示例代码如下所示:
simpleQuery search = new SimpleQuery (criteria) ;

SolrDataQuery拥有如图13-7所示的几种实现类,大致分5类:FilterQuery 、TermsQuery、SimpleQuery(普通的简单查询)、FacetQuery、HighlingQuery。

3.使用SolrTemplate执行任意Solr请求
当Spring Data Solr提供的API无法表示你所要执行的Solr查询,或者你想要执行的Solr查询表达式过于复杂而你对Spring Data Solr API不够熟悉,那么此时你需要使用SolrCallback接口。SolrCallback接口只有一个接口方法doInSolr(SolrClient solrClient),入参是SolrClient实例对象,有了SolrClient对象,就可以以纯粹SolrJ的方式来与Solr Server进行交互了,从而彻底摆脱Spring Data Solr框架的束缚,当你对Spring Data Solr不熟悉,或者对这种为了面向对象而面向对象的编程方式已经厌烦,此时你可以实现SolrCallback接口并重写doInSolr方法,这里的泛型T即返回值类型。实现 SolrCallback接口你可以新建一个类,不过一般推荐采用匿名实现类的方式比较方便。SolrTemplate 工具类提供了一个execute(SolrCallback action)方法,它接收一个SolrCallback接口实现类,然后将SolrClient实例对象传入doInSolr方法,然后执行dolnSolr方法返回响应结果。使用示例如下所示:

第14章SolrCloud
通过第14 章,你将可以学习到以下内容:
口SolrCloud快速人门;
口Core与Collection的区别;
口使用Zookeeper管理SolrCloud配置文件;
口SolrCloud的分布式索引和查询;
口使用Solr Collection API;
口Solr索引主从复制;
口跨数据中心的索引复制(CDCR)。

14.1 SolrCloud 快速人门
SolrCloud是设计用来处理跨多台服务器的分布式索引和查询工具,具有高可用性、可
扩展性、自动容错性的特点。在SolrCloud中,索引数据被分成多个Shard (分片),而每个
Shard可以托管在多台机器上,同时为每个Shard提供副本冗余来提供可扩展性和自动容错性。SolrCloud利用Zookeeper来管理集群中所有节点以及集中管理集群配置文件。

14.2 SolrCloud工作原理
14.2.1 SolrCloud的核心概念
SolrCloud集群是由构建在物理概念之上的一些逻辑概念层组成。

1.逻辑概念
一个Solr集群上可以承载多个Collection的索引文档。一个Collection由多个索引文档
组成,而一个Collection可以被分割成多个Shard (分片),每个分片中包含了这个Collection中的部分索引文档。理论上讲,一个Collection包含Shard的个数决定了Collection 中能够
合理包含的索引文档总个数以及单个查询请求可能的并行度。

2.物理概念
一个Solr集群由一个或多个Solr节点组成,每个Solr节点运行着一个 Solr Server实例。
每个Solr节点可以包含多个Core,而集群中的每个Core其实就是某个逻辑概念上的分片对应的某一个物理副本。每个副本使用同一套配置文件, 每个Shard的副本个数决定了Collection的冗余级别、集群的自动容错度以及高负载情况下的并发查询请求数量。

14.2.2 SolrCloud 中的Shard
当单个节点上的Collection包含的索引文档过大时,你可以通过创建多个Shard进行
分别存储。一个Shard是一个Collection的逻辑部分,它包含了Collection中的部分索引
文档,因此Collection中的每个索引文档都直接属于一个Shard,每个索引文档分配给哪
个Shard则取决于你的Shard分片策略。比如,你有一个Collection,其中每个索引文档
中都有一个“city"域,那么你可以将city域值相同的索引文档分配给同-一个Shard。不
同的Collection可以简单地对每个索引文档的UniqueKey进行Hash计算从而决定它属于
哪个Shard。

SolrCloud解决了上面所有问题,SolrCloud 支持自动的分布式索引和查询,同时借助
Zookeeper提供了自动故障转移和负载均衡功能。此外,每个Shard同时还可以拥有多个
Replica以提供额外的系统健壮性。在SolrCloud模式下,没有Master 和Slave的概念,取
而代之的是,每个Shard由至少一个物理存在的Replica组成,其中一个Replica会被选
举为Leader。如果-一个Leader挂掉了,Zookeeper会自动以其他Replica节点中选举新的
Leader,从而保证集群高可用。当你发送一个索引文档到Solr集群的任意一- 个节点请求创
建索引时,Solr 集群首先判断索引文档属于哪个Shard,然后确定该Shard的Leader所在节
点,紧接着索引文档会转发到当前Leader节点上进行索引,最后Leader节点会将索引文档更新广播给其他Replica。
14.2.3 Collection VS Core
前面章节中,我们一直都在使用Solr Core。概括来讲,一个Solr Core是Solr Server中
被唯一命名、可配置的、受管理的索引集合的总称。一-个SolrServer实例可以拥有多个Solr
Core。Solr中的Core通常是通过定义不同的Schema来分割索引文档的。SolrCloud中的
Collection虽然也是索引集合的总称,但它是逻辑概念。Collection 会将索引文档分割成多个Shard,这些Shard会分布在多个Solr Server节点之上,每个Shard的Replica才是真正物理存在的Core,而Collection并没有物理存在于任意-个节点上。在SolrCloud模式下,你不
需要再关心物理存在的Core,你的关注点应该是Shard。

正如单节点的SolrServer可以托管多个SolrCore,SolrCloud集群也可以托管多个
Collection,如果你需要使用不同的Schema来表示不同的Document,此时你需要使用不同
的Collection。下面就SolrCloud中的一些常见术语做简单解释,便于大家能够理解它们的
表示含,义以及各个概念术语之间的区别与联系:

口Collection:同一类型的索引文档的集合,但是这些索引文档并不实际存储在同一台
机器上,它们通常会被分割成多个分片,然后每个分片会创建多个副本,所以实际
存储的是副本,同-个Shard下的每个副本会分散到多个节点上存储。

口Shard:单个Collction的逻辑划分,划分出来的每一份 称之为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,在
集群环境下,每个SolrServer实例下管理的每个Core其实就是Collection下某个
Shard的某-一个 Replica (副本)。

总结: SolrCloud 下的Collection就好比跨多台服务器的Multi Core (多Core),这里说
的Multi Core其实就是每台服务器上的Replica (副本)。

14.2.4索引文档路由
Solr允许通过指定router.name参数来设置文档路由实现,如果使用默认的“compo-
siteld" Router (自动路由),你可以创建并发送一个索引文档ID带有前缀的索引文档,根据
这个前缀进行Hash计算从而确定该索引文档应该发送到哪个Shard进行索引。这个前缀可
以是任意字符串,而不必是Shard的名称。比如,你想要将“Customer”相关的索引文档划
分为一个Shard,那么你可以使用Costomer的name或者ID作为索引文档ID的前缀。比如
你的客户是“IBM”的,而你的Document ID是“12345”, 那么你可以设置你的Document
ID为“IBM!12345”。这里的感叹号(!)是用来界定前缀字符和真实Document ID的。这样
在索引创建时就能建立索引文档与所属Shard之间的映射关系。在查询时,你可以通过显式
设置_ rout_ 参数来明确指定想要查询的索引文档的ID前缀字符,这样Solr 会根据你提供的前缀字符知道具体的Shard,直接将查询请求转发到目标Shard的Leader节点上。在某些情况下,这种方式能够提升查询性能,因为它避免了所有Shard之间查询的网络延迟。

在查询时,你可以指定_ route_ _ 参数或者shard.keys参数(不过shard.keys参数已经被
标记废弃不建议使用)来明确指定在哪个Shard.上执行查询,Solr 会自动根据该参数计算出
你想要查询的Shard的逻辑ID;你也可以通过shards参数明确指定Shard的逻辑ID。以下
是几个_ route_ 参数的使用示例:

当你创建了Collection并且明确定义了使用ImplicitRouter(隐式路由),那么可以额外
指定一个router.field参数,通过这个field来唯一标识每 个Document属于哪个Shard。如果某个索引文档的这个域缺失了,那么这个索引文档将会被拒绝。如果用户在创建Collection时指定了numShards 参数,那么系统会自动采用compositeld路由,否则会自动使用Implicit 隐式路由。你可以通过Zookeeper.上clusterstate.json文件中的router属性来查看一个Collection所采用的路由类型。

14.2.5 Shard 的几种状态
SolrCloud中的Shard分以下几种状态:
口ACTIVE:活动状态,一个Shard的默认状态。

口INACTIVE:当-一个Shard被成功分出之后,它就暂时处于INACTIVE (非活动)状态。

口 CONSTRUCTION :当一个Shard被重新分割,在分割处理过程中,新分出来的
子Shard就处于CONSTRUCTION状态。处于该状态下的Shard仍然能够接收来自
Shard Leader转发的更新请求,然而它不能参与分布式查询。

口 RECOVERY :当一个Shard的子Shard需要创建Replica以满足Collection上设置的
Replica总个数时,该Shard就处于RECOVERY状态。处于此状态的Shard仍然能
够接收来自ShardLeader转发的更新请求,然而它不能参与分布式查询。
虽然对于用户而言,根本不用关心Shard的内在状态,但是能够熟悉Shard的内部状
态,当SolrCloud出现问题时,有助于你更好地分析问题并予以解决。

14.2.6 Replica 的几种状态
SolrCloud中的Replica 拥有以下几种状态:
0 ACTIVE (活动状态):当一个Replica处于ACTIVE状态,即表明当前Replica已经准
备好,可以接收更新和查询请求了。当Replica所在的Solr节点崩溃了,该Replica的
状态信息可能还遗留在Zookeeper中,因此判断-一个Replica 是否真正处于ACTIVE状
态,必须通过调用Replica.getNodeNameO或者ClusterState.liveNodesContain(String)进
行确认。ACTIVE是Replica 的默认状态。

0 DOWN (宕机状态): DOWN是RECOVERING之前的第一个状态,当一个Solr节点处于DOWN状态时,它会积极地尝试转人RECOVERING状态。当然- -个 Solr节
点宕机之后,属于该Solr节点管理的Replica会自动进人DOWN状态。但是你不能
完全依赖此状态,因为它并不- -定可靠。

0 RECOVERING ( 数据恢复状态):此状态表示Replica正从Shard Leader上执行索引
恢复操作。索引恢复操作分两种: peer- sync (Update Log增量同步)和full replication
(索引全量恢复)。

口RECOVERY_ FAILED (数据恢复失败状态):当Replica尝试进行数据恢复操作,但
是尝试失败时会进人此状态。如果当前Replica 所在Solr节点不在Zookeeper的/
live_nodes下,那么该Replica的状态会被丢弃。因为它不在Solr集群中,记录该状
态信息已经没有意义。.

14.2.7 Shard 分割
当你在SolrCloud环境下创建一个Collection,你需要决定初始化Shard的个数,然
而,你很难预先知道需要几个Shard,特别是当你的需求随时都在变化的时候。任意的分
割Shard功能由Solr的Collections API提供,它允许你将一个Shard分割成两份,而原来:
的Shard不做任何改动,你可以随时在未来的某个时间点将旧Shard卸载。关于如何使用
Shard的分割操作,请查阅Solr的Collection API,后续章节会做介绍。

14.2.8 SolrCloud 里的自动提交
在大多数情况下,当运行在SolrCloud模式时,Client请求创建索引不需要显式的
commit,此时你应该配置自动硬提交(此时openSearcher应该为false)和自动软提交,使得:
最近更新对于查询可见。要想强制执行这种自动提交方式,需要你的所有Client都将索引数
据发送给SolrCloud。然而,这并不是很好执行,因此Solr提供了IgnoreCommitOptimizeU
pdateProcessorFactory,它允许你在Client应用程序里忽略显式的提交或索引优化。激活这个请求进程,需要在solrconfig.xml中添加如下配置:

14.2.9 SolrCloud的分布式查询请求
当一个Solr节点接收到–个查询请求,这个请求会被路由到被查询的Collection的一
些Shard的Replica.上。被选中的Replica将扮演aggregator (聚合者)的角色,它负责创建
内部请求去随机选择Collection的每个Shard.上的一些replica,协调每个replica的响应信
息,并按照需要发起内部的子请求,比如重新改善Facet的值,或者请求额外的存储域,最
终汇总构造一个完整的响应信息返回给Client。

1.分布式Query请求流程
当你通过CloudSolrClient发起一个Solr查询请求,首先在requestWithRetryOnStale
State方法中会通过配置你的zkHost参数连接Zookeeper,从而获取此时Solr集群的状态
信息。其次获取用户传入的collection参数,如果用户没有传入collection参数,那么以
查询请求BaseURL里的collection为准。然后传入SolrRequest对象和collection参数转到
sendRequest方法。紧接着通过Zookeeper获取Solr集群状态信息,从Solr集群的状态信息中获取所有Live Node (活跃节点)信息,因为只有对活跃节点执行查询才有意义。最后遍
历所有活跃节点获取它下面的所有Shard信息,每个Shard的状态信息大致如下:

2.分布式Update请求流程

3.指定查询的目标Shard
使用SolrCloud的其中-一个好处就是你可以对一个分布在多个Shard.上的很大的
Collection进行查询。比如,有些时候你可能只对某些Shard.上的索引文档感兴趣,通过SolrCloud,你可以选择是在所有的索引数据上执行查询还是仅仅只是部分Shard.上

如果你只想要在某一个Shard.上查询,可以通过该Shard的逻辑ID来指定想要查询的
Shard,示例如下:
http :localhost: 8080/so1r/gett ingstarted/select ?q=*: *&shards=shard1

14.2.11 Zookeeper
ZooKeeper是分布式系统里的协调框架,Solr 使用ZooKeeper来实现3个关键操作:
口配置文件的集中存储与分发;
0探测和通知集群状态更新;
0 Shard Leader选举。
14.3 SolrCloud 集群搭建

14.4 SolrCloud 的基本操作
14.4.2 创建Collection
14.4.3 删除Collection
14.4.4 启动Solr
14.4.5 停止Solr
14.4.6 查看Solr状态
查看Solr状态的命令是solrstatus,此命令没有任何可选的输人参数,它会返回当前机
器.上运行中的所有Solr实例的状态信息。
14.4.7。Collection 健康检测
14.4.8 管理 Zookeeper 上的配置文件
14.5 SlorCloud 配置详解
与 SolrCloud 紧密相关的两个配置文件就是 { S O L R H O M E }目录下 s o l r . x m l 和 z o o . c f g 两个配置。 s o l r . x m l 必须处于 {SOLR_HOME}目录下 solr.xml 和 zoo. cfg 两个配置。 solr.xml 必须处于 SOLRHOME}目录下solr.xmlzoo.cfg两个配置。solr.xml必须处于{SOLR_HOME}根目录下,除非你已经将 solr.xml 上传到 Zoo keeper 上。 zoo.cfg 其实就是 Zookeeper 的配置文件,对 Zookeeper 熟悉的同学来说,应 该不会陌生。
14.5.1 solr.xml详解
下面详细列举了core.properties 中支持的配置属性,如表14-2所示。

你还可以在 core.properties 属性文件中自定义任何的属性,比如设置 key=value,然后 你就可以在 solrconfig且nl 中以”${key :默认值}”的方式来引用 core.properties 中 自定义的 属性。

其中<solr>元素是 solr.xml 的根元素,它支持如表 14-3 所示的几个可以配置的属性

<sol re loud>元素是与 SolrCloud 相关的一些配置参数,除非在 Solr 实例启动时指定 了-DzkRun 或-DzkHost 参数,否则表 14-4 列举的关于 SolrCloud 的参数将会被忽略。 即只 有指定了 zkRun 或 zkHost 参数才会开启 SolrCloud 模式,其中 zkRun 参数表示使用 Solr 内 嵌的 Zookeeper。

表 14-4 <SolrCloud>参数表

14.5.2 zoo.cfg 详解

14.6 SolrCloud 分布式索引
对于客户端而言, 在 SolrCloud 模式下进行文档索引并没有什么太大不同。 但是你可以 为你的客户端程序进行一些优化以提升性能,以及提供对近实时查询进行支持。 对于 Solr Server 端而言,分布式索引已经发生巨大改变。 SolrCloud 会根据索引文档确 定它所属的 Shard, 并将其转发到相应的 Shard Leader 节点上进行索引 。

14.6.1 添加索引文档到 SolrCloud
往 SolrCloud 中添加索引文档,可以通过 Solr 的后台管理界面添加,或通过 Solr 提供 的 post.jar 进行添加,这些操作都跟单机模式下没什么太大不同。 这里我们主要注重学习如 何使用 SolrJ 往 SolrCloud 里添加索引文档进行索引。

14.6.2 Solr Cloud 里的近实时查询
14. 7 SolrCloud 分布式查询
一旦你对你的索引数据进行了分片,就会有个问题:你必须对所有的 Shard 进行查询才 能获取到完整的结果集。 针对指定的 Collection 跨多个 Shard (分片)进行查询,就是我们 常说的分布式查询。 Solr 会根据用户是否传入 distrib 参数或者 shards 参数或者 route 参数 来判断当前是否为分布式查询。 distrib=true 表示开启分布式查询。 当你运行在 SolrCloud 模 式下, distrib 参数默认是 true。 如果你将 distrib 设置为 false,那么就只是本地索引查询了。 对于客户端用户而言,使用 SolrJ 执行分布式查询与传统的单机模式下,没有什么太大 的不同,不同的只是你可以指定 shards 参数,即明确指定在哪些 Shard 上执行查询,默认会 在 Collection 下的所有 Shard 上查询。

然而,并不是所有的 Solr 查询功能都能在 SolrCloud 模式下正常工作。 尤其是, SolrCioud 的分布式查询有以下几点限制你必须清楚:

口 IDF 是基于本地索引进行统计,而不是所有 Shard 上的索引数据。 IDF 值会用于索引 文档打分。 因此,在分布式查询中,索引文档的评分可能会有所偏差。 因为默认情况下,索引文档是随机跨多个 Shard 分布在多个 Solr 节点上 Shard I 上某个 Term 的 IDF 通常是近似于该 Term 在所有 Shard 上的 IDF 值。 如果想要更高的统计精确度, 那么你需要配置ExactStatsCache。

口 Solr 中的 Join 查询不支持跨多个 Shard 请求,仅仅支持单个 Shard 多个 Replica 的情 况。 但是, 当你的数据量巨大时,只划分单个 shard 显然会造成 Solr查询延迟,性能 变差。 注意,这里说的 Join 查询指的是使用{!join fromlndex=xxtolndex=xxx from=pid to=id}这种 Join Query Parser 查询语法的查询,不包括 Block Join。 Solr 中的 Block Join 支持跨多个 Shard 查询。 通过查阅 Solr 6.2.l 版本中的 ScoreJoinQParserPlugin 类 的源码我们可以得知(如图 14-13 所示), Join 查询依然不支持跨多个 Shard 查询。 即 便是单机版的 Join 查询仍有以下几点限制:
• from 参数对应 的域建议定义为 type=”string” 且 docValues=" true”。 假如该域的 doc Values=false,虽然这样也能执行join 查询,但是会更消耗内存。 · 数字类型的 DocValues 对于 from 参数暂不支持,支持的 DocValues 类型有SORTED、 SORTED SET、 BINARY。 • to 参数对应的域应该设置为 indexed="true”,并且建议 multiValued=false,即单值 域,即便你设置为多值域也会被当作单值域处理。

分布式环境下的 Join 查询确实是一个挑战,因为它需要跨多台服务器与大量的索引数 据进行比较。 但是随着 Solr 版本的不断迭代,应该会慢慢完善。

口 每个索引文档必须要有 UniqueKey。

口如果 Solr 发现有 Document ID 重复, Solr 会选择第一个,丢弃后面的。

口如果一个索引提交发生在分布式查询的第一和第二阶段之间,索引数据随时可能不 同步。 这可能会导致刚开始匹配查询,索引数据随时可能改变不匹配查询但是最后 仍然被查询到。 这种情况很少发生,也只可能发生在单个查询请求中。

口 Shard 的数量受 Http GET 方法的 URI 的字符长度限制,大部分的 Web Se凹er 支持至少 4000 字符,但是很多 Web Se凹er会限制 URI 的长度来降低受到 DOS 攻击的安全隐患。

14.8 SolrCloud Collection API
在第 13 章当中,我们已经学习了 Solr 中的 CoreAPI, 通过 Core API 你能管理你的
Solr Core。 同理, SolrCloud 也提供了 CollectionAPI, 通过 Collection API,你也可以管理 自己的 Collection,比如创建删除 Collection、为 Collection 创建别名,同时还提供了 Shard 分割等功能。

14.8.2 Shard常用操作API

14.8.3 Replica 常用操作API

14.8.4 集群管理 API

14.9 Solr 索引主从复制
所谓 Index Replication (索引复制)就是将 Master 节点上的所有索引数据复制到一个或 多个 Slave 节点上。 Master节点用于接收 updae 请求,而所有的查询请求有 Slave 节点处理。 这种读写分离使得 Solr 能够扩展支持大规模的查询请求。

14.9. 1 索引复制简介
Solr 里的基于 Java 实现、 通过 HTTP 协议工作的索引复制功能具有以下几点特性: 0 Solr 的索引主从复制不需要借助外部脚本。
口 只需要在 solrconfig.xml 中进行配置。
口 除了能够复制索引数据同时支持复制配置文件。
口 能够使用相同的配置跨平台工作。 口 不依赖于特定操作系统的文件系统特性,比如硬链接。
口 与 Solr 紧密集成, Solr 提供了一个后台管理界面来细粒度的全方位的控制 Index Replication (索引复制)。
口 Solr 里的索引复制功能被实现为一个 Request Handler,配置索引复制理论上与配置 Solr 里的其他普通 Request Handler 基本相似。

图 14-14形象展示了 Solr 中的索引复制的基础架构, M部ter 的索引会被复制到多个 Slave上。

尽管在 Solr 集群中并没有显式的 Master/Slave (主从)概念,但是 SolrCoud 仍然使用 Replication Handler 来处理 Shard 的恢复。

14.9.2 索引复制的术语
表 14-13 详细列举了与 Solr 索引复制相关的概念术语。

14.9.3 索引复制的配置

14.9.4 配置索引复制中继器

14.9.5 索引复制工作机制

14.10 跨数据中心的索引复制(CDCR)
SolrCloud 架构在某些情况下不太适用,比如集群节点之间需要昂贵的网络连接开销 时。 为了防止 SolrCloud 整个数据中心崩溃, Solr 设计了 CDCR 功能特性来缓解这些问题。 注意,这是 Solr6 中的新功能。

14.10.1 什么是 CDCR
CDCR 其实就是两个 SolrCloud 集群之间的索引复制, ‘这里的数据中心就好比两个 Solr 集群。 这里分 Source Data Center 和 Target Data Center,即源数据中心和目标数据中心,两 者都能接收查询请求,但是只有源数据中心处理 Update 请求。 源数据中心会主动将更新复 制到目标数据中心,这种数据更新复制几乎是实时的,而且可以配置按照指定间隔周期性发 送更新到目标数据中心。

14.11 本章总结
希望大家通过本章的学习都能深刻理解 SolrC!oud 并能够熟悉地使用它来处理大规模 的索引文档以达到可扩展性、可伸缩性、高可用性、高可靠性。 总体上来说,我们首先从 SolrCloud 快速入门开始进入 SolrCloud 世界,然后我们在 SolrCloud 工作原理章节理解了 SolrCloud 底层到底是如何工作的。 然后我带领大家分别在 Tomcat 和 Jetty Web 容器下搭建 了 SolrCloud 集群,随之我们掌握了 SolrCloud 的基本操作,例如如何创建删除 Collection 以及如何管理 Zookeeper 上的配置文件。为了大家更好地配置 SolrCloud,我们又详细了解 了 SolrCloud 相关的配置文件。 然后我们继续深入学习了 SolrCloud 中如何创建索引以及如 何进行分布式查询。 由于大多数情况下操作 Solr 都是借助 SolrJ,我们有必要掌握 SolrCloud 中提供的 Collection A凹,通过此 API 你能够以编程的方式操作 SolrCloud。 虽然 SolrCloud 的架构已经很流行, Solr 索引复制的主从架构仿佛显得被冷落,但是这并不意味着 Solr 索 引复制的主从架构模式一无是处了。 如果你的项目场景比较适合使用这种主从复制架构, 那么你可以选择性地学习该章节的知识点。 最后我们还介绍了 Solr 6 中新提出来的 CDCR 架构,它支持 Collection 级别的跨数据中心的索引复制,它缓解了 Solr 主从复制中的一个 Master 服务于多个 Slave 存在的网络性能瓶颈问题,避免 SolrCloud 整个集群挂掉不可用的 尴尬。

第15章Solr性能优化
影响Solr性能的一个主要因素就是内存
15.1 Schema设计的注意事项
主要是减少索引的体积,一些不需要的结构数据就不用
15.2 Solr索引更新与提交的优化建议
Solr中的索引更新与提交一般不建议显式的调用commit()进行硬提交,请在solrconfig.xml中配置自动硬/软提交。而且在SolrCoud模式下,建议每个Replica 的自动硬/软提交的配置保持一致。
此外你应该在solrconfig.xml中适当调大ramBufferSizeMB和 maxBufferedDocs参数值,以减缓Solr自动触发提交的频率。客户端在提交索引文档时,建议采用批量软提交方式添加索引文档,比如每1000个索引文档为一个批次,目的是尽量减少commit次数。
15.3索引合并性能调优
索引提交除了用户显式的执行commit 操作之外,ramBufferSizeMB或者maxBufferedDocs参数达到限定的阈值之后也会自动触发索引提交。因此,为了降低索引合并的频率,应该加大ramBufferSizeMB和 maxBufferedDocs参数值,并且尽量降低显式提交的频率,比如采用批量commit,或者直接在solrconfig.xml中配置自动提交并控制自动提交的频率,避免显式提交
15.4索引优化的注意事项
你需要定期优化你的索引,因为Solr的索引提交是每次生成新的索引段文件,段文件数量越多,会严重影响查询性能。但是索引优化操作的执行开销很大,建议在凌晨1~2点钟再执行此操作。索引优化执行频率与你的索引更新频率相关,如果你的索引数据频繁在更新,那么你需要适当调整索引优化次数。
香景月防沫
在Master/Slave主从索引复制架构中,如果你在Master节点上执行索引优化,将段文件合并为一个,此时Slave与 Master进行数据同步需要复制一个很大的段文件,这会增大索引同步时间,从而会影响Slave上的查询响应速度。所以通常一般建议在Slave上进行索引优化,而不是 Master 上。
15.5 Solr缓存
Solr缓存在Solr中扮演着重要的角色,它很大程度上决定了你的Solr查询性能。Solr 内置提供了4种类型缓存:filterCache、documentCache、queryResultCache、fieldValueCache。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值