Solr部署:SolrCloud分布式请求路由与容错机制深度解析
概述
当Solr节点接收到搜索请求时,请求会自动路由到作为被搜索集合一部分的分片副本。选定的副本充当聚合器:它创建对集合中每个分片的随机选择副本的内部请求,协调响应,根据需要发出任何后续内部请求(例如,细化分面值或请求其他存储字段),并为客户端构造最终响应。
分布式请求处理流程
请求路由机制
- 接收请求:Solr节点接收搜索请求
- 选择副本:自动路由到集合分片的副本
- 聚合处理:被选中的副本作为聚合器执行以下操作:
- 创建对每个分片随机副本的内部请求
- 协调各分片的响应
- 执行后续内部请求(分面细化、字段获取等)
- 构造客户端的最终响应
负载均衡机制
SolrCloud集群中每个节点都会在集合的所有副本之间进行读请求负载均衡。您可能仍需要外部负载均衡器与集群通信,或需要能够读取和交互Solr在ZooKeeper中的元数据的客户端。
智能客户端:Solr提供智能Java SolrJ客户端CloudSolrClient
,具备此功能。
查询容错机制
基本容错原理
即使集群中某些节点离线或无法访问,只要Solr节点能与每个分片的至少一个副本通信,或者在用户通过shards
或_route_
参数限制搜索时能与每个相关分片的一个副本通信,Solr节点就能正确响应搜索请求。
关键要点:每个分片的副本越多,Solr集群在节点故障时处理搜索结果的可能性就越大。
zkConnected参数
工作机制
Solr节点在收到请求时,即使无法与ZooKeeper通信,只要它能与知道的每个分片的至少一个副本通信,就会返回搜索请求的结果。
潜在风险
这种行为从容错角度来说通常是首选的,但如果集合结构发生重大变化而节点未通过ZooKeeper得到通知(如分片已被添加、删除或分割为子分片),可能导致陈旧或不正确的结果。
响应头标识
每个搜索响应都包含zkConnected
头,指示处理请求的节点在处理时是否连接到ZooKeeper:
1 | { |
避免陈旧结果
要防止在请求服务节点无法与ZooKeeper通信时出现陈旧或不正确的结果,请将shards.tolerant
参数设置为requireZkConnected
。这将导致请求失败而不是将zkConnected
头设置为false
。
shards.tolerant参数详解
基本行为
当查询的一个或多个分片不可用时,Solr的默认行为是失败请求。但是,许多用例中部分结果是可接受的,因此Solr提供布尔型shards.tolerant
参数(默认false
)。
参数值选项
- true:允许返回部分结果
- false:默认值,分片不可用时请求失败
- requireZkConnected:要求ZooKeeper连接,否则请求失败
部分结果处理
当shards.tolerant=true
时,可能返回部分结果。如果返回的响应不包含所有适当分片的结果,响应头会包含名为partialResults
的特殊标志:
1 | { |
requireZkConnected模式
当shards.tolerant=requireZkConnected
时:
- 如果服务搜索请求的节点无法与ZooKeeper通信,请求将失败
- 当一个或多个查询分片完全不可用时,也会导致请求失败(类似于
shards.tolerant=false
)
详细信息获取
客户端可以与shards.tolerant
参数一起指定shards.info
来检索更细粒度的详细信息。
distrib.singlePass参数
功能说明
如果设置为true
,distrib.singlePass
参数会改变分布式搜索算法,在第一阶段本身从每个分片获取所有请求的存储字段。这消除了获取存储字段的第二次请求需求。
性能考虑
适用场景:
- 请求包含小值的非常少数字段时可能更快
不适用场景:
- 请求大字段或大量字段时,从所有分片通过网络获取它们的开销可能使请求比正常分布式搜索路径更慢
重要限制:
- 此优化仅适用于分布式搜索
- 某些功能如分面搜索可能为细化等目的进行额外的网络请求
查询路由控制
限制查询的分片
默认行为
使用SolrCloud的优势之一是能够查询分布在各个分片上的非常大的集合。在某些情况下,您可能配置了特定的文档路由。您可以选择搜索所有数据或仅搜索其中的部分。
全分片查询
由于SolrCloud自动负载均衡查询,对集合的所有分片的查询就是不定义shards
参数的查询:
1 | http://localhost:8983/solr/gettingstarted/select?q=*:* |
这与用户管理的集群形成对比,后者需要shards
参数来分发查询。
单分片查询
要将查询限制为仅一个分片,使用shards
参数通过逻辑ID指定分片:
1 | http://localhost:8983/solr/gettingstarted/select?q=*:*&shards=shard1 |
多分片查询
如果要搜索一组分片,可以在一个请求中指定每个用逗号分隔的分片:
1 | http://localhost:8983/solr/gettingstarted/select?q=*:*&shards=shard1,shard2 |
副本级别控制
指定特定副本:
1 | http://localhost:8983/solr/gettingstarted/select?q=*:*&shards=localhost:7574/solr/gettingstarted,localhost:8983/solr/gettingstarted |
负载均衡副本选择(使用管道符号|分隔不同副本ID):
1 | http://localhost:8983/solr/gettingstarted/select?q=*:*&shards=localhost:7574/solr/gettingstarted|localhost:7500/solr/gettingstarted |
混合配置:
1 | http://localhost:8983/solr/gettingstarted/select?q=*:*&shards=shard1,localhost:7574/solr/gettingstarted|localhost:7500/solr/gettingstarted |
shards.preference参数
基本概念
Solr允许传递可选字符串参数shards.preference
,指示分布式查询应按给定的优先顺序对每个分片中的可用副本进行排序。
语法:shards.preference=property:value
重要限制
shards.preference
仅在使用SolrJ客户端时支持单分片场景。不使用SolrJ客户端的查询无法在单分片集合中使用shards.preference
。
可用属性
replica.type
指定一个或多个首选副本类型:
选项:PULL
、TLOG
、NRT
的任意组合
示例:
1 | shards.preference=replica.type:PULL |
replica.location
指定一个或多个首选副本位置:
格式:以http://hostname:port
开头的位置
特殊值:local
表示与处理查询的Solr实例相同的任何本地副本
示例:
1 | shards.preference=replica.location:local |
local值的优势:
- 避免大量数据在网络上移动
- 减少性能退化副本的影响
- 最适用于少量分片和多副本的集合
注意事项:
- 仅在对托管集合副本的所有节点进行负载均衡时使用
- 否则可能在集群中引入热点
replica.base
定义优先级等价副本集合之间的后备排序:
random(默认):为每个请求随机洗牌副本
stable:dividend:paramName:使用参数值作为除数确定副本偏好顺序
stable[:hash[:paramName]]:对参数值进行哈希确定副本偏好顺序
示例:
1 | shards.preference=replica.base:stable:hash:sessionId&sessionId=abc123 |
replica.leader
基于leader状态偏好副本:
选项:true
或false
示例:
1 | shards.preference=replica.leader:false |
node.sysprop
将查询路由到具有相同系统属性的节点:
示例:
1 | shards.preference=node.sysprop:sysprop.rack |
综合示例
复杂偏好组合:
1 | shards.preference=replica.location:local,replica.type:PULL,replica.type:TLOG |
collection参数
collection
参数允许指定执行查询的一个或多个集合。这允许一次查询多个集合,Solr的分布式功能将跨集合工作。
1 | http://localhost:8983/solr/collection1/select?collection=collection1,collection2,collection3 |
_route_参数
_route_
参数可用于指定路由键,用于确定相应的分片。
使用场景:如果文档具有唯一键”user1!123”,则指定路由键为_route_=user1!
(注意尾随’!’字符)将把请求路由到托管该用户的分片。
多路由键:可以指定用逗号分隔的多个路由键
示例:
1 | http://localhost:8983/solr/collection1/select?q=*:*&_route_=user1! |
近实时(NRT)使用案例
NRT概念
近实时(NRT)搜索意味着文档在索引后很快就可用于搜索。NRT搜索是SolrCloud的主要功能之一,在用户管理的集群或单节点安装中很少尝试。
提交策略
文档持久性和可搜索性由commits
控制。”近实时”中的”近”是可配置的以满足应用程序需求。
提交类型:
- 硬提交:通常配置为
openSearcher=false
- 软提交:配置为使文档对搜索可见
配置建议:
通常建议在solrconfig.xml
中配置提交策略并避免外部发出提交。
性能优化
后台任务:提交发生时,启动各种后台任务(如段合并),但不会阻止对索引的额外更新或延迟文档的搜索可用性。
缓存和预热配置:配置NRT时,特别注意缓存和自动预热设置,它们对NRT性能有重大影响。
极短自动提交间隔:考虑完全禁用缓存和自动预热。
ShardHandlerFactory配置
基本概念
需要对分布式搜索中的并发性和线程池进行细粒度控制的管理员可以在SearchHandler配置中定义shardHandlerFactory
。
实现选择
HttpShardHandlerFactory(默认):
- 偏向吞吐量而非延迟
ParallelShardHandlerFactory:
- 可选实现
- 对于有许多分片的集合可能更适合
配置选项
两种实现都提供其他shardHandlerFactory
设置:
- 线程池大小
- 网络超时
- 其他分布式搜索行为调优参数
分布式逆文档频率(IDF)
背景问题
计算相关性需要文档和术语统计信息。在分布式系统中,这些统计信息可能因节点而异,从而给评分计算带来偏差或不准确性。
statsCache实现
Solr将文档和术语统计信息存储在名为statsCache
的缓存中。开箱即用的四种实现:
LocalStatsCache(默认)
- 仅使用本地术语和文档统计信息计算相关性
- 在分片间术语分布均匀的情况下效果合理
- 未配置
<statsCache>
时的默认选项
ExactStatsCache
- 使用全局值(跨集合)计算文档频率
- 如果跨节点精确评分对实现很重要,建议选择此选项
ExactSharedStatsCache
- 功能类似
ExactStatsCache
- 全局统计信息可重用于具有相同术语的后续请求
LRUStatsCache
- 使用最近最少使用的缓存保存全局统计信息
- 在请求间共享统计信息
配置示例
在solrconfig.xml
中设置<statsCache>
:
1 | <statsCache class="org.apache.solr.search.stats.ExactStatsCache"/> |
最佳实践建议
容错配置策略
- 副本规划:每个分片配置多个副本以提高可用性
- 监控配置:启用
zkConnected
和partialResults
监控 - 容错参数:根据业务需求配置
shards.tolerant
性能优化策略
- 副本偏好:根据网络拓扑和硬件配置合理设置
shards.preference
- 缓存策略:NRT场景下仔细调优缓存和预热设置
- 统计信息:根据精确度要求选择适当的
statsCache
实现
运维监控要点
- 健康检查:监控ZooKeeper连接状态
- 性能指标:跟踪查询延迟和吞吐量
- 容错状态:监控部分结果出现频率
- 资源使用:监控线程池和网络资源使用
总结
SolrCloud的分布式请求处理机制为大规模搜索应用提供了强大的容错和负载均衡能力。通过合理配置查询路由、容错参数和性能调优选项,可以构建高可用、高性能的搜索集群。
理解分布式请求的工作原理和各种配置参数的作用,对于设计和运维生产级SolrCloud集群至关重要。结合具体的业务需求和系统特点,选择合适的配置策略,可以最大化发挥SolrCloud分布式架构的优势。