SolrCloud 分片和索引
如果你的集合对于一个节点来说太大,你可以通过创建多个分片将其分解并按部分存储。
分片是集合的逻辑分区,其中包含集合中部分文档,以便集合中的每个文档都恰好包含在一个分片中。集合中的每个文档包含在哪个分片中取决于该集合的整体分片策略。
例如,你可能有一个集合,其中每个文档的“国家/地区”字段决定了它属于哪个分片,以便来自同一国家/地区的文档共置。另一个集合可能只是对每个文档的 uniqueKey 使用“哈希”来确定其分片。
在 SolrCloud 之前,Solr 支持分布式搜索,它允许在多个分片上执行一个查询,以便针对整个 Solr 索引执行查询,并且不会从搜索结果中遗漏任何文档。因此,跨分片拆分索引并不是一个专门的 SolrCloud 概念。但是,分布式方法存在几个问题,需要通过 SolrCloud 进行改进
-
将索引拆分成分片有点手动。
-
不支持分布式索引,这意味着你需要明确地将文档发送到特定分片;Solr 无法自行确定将文档发送到哪些分片。
-
没有负载平衡或故障转移,因此,如果你收到大量查询,你需要确定将它们发送到哪里,如果一个分片死了,它就消失了。
SolrCloud 解决这些限制。它支持自动分发索引进程和查询,并且 ZooKeeper 提供故障转移和负载平衡。此外,每个分片都可以有多个副本以提高可靠性。
领导者和副本
在 SolrCloud 中,没有领导者或跟随者。相反,每个分片至少包含一个物理副本,其中恰好一个为领导者。领导者会自动选举产生,最初是先到先得,然后基于 https://zookeeper.net.cn/doc/r3.9.1/recipes.html#sc_leaderElection 中描述的 ZooKeeper 进程。
如果领导者宕机,其他副本之一会自动选为新领导者。
当文档发送到 Solr 节点进行索引时,系统首先确定该文档属于哪个分片,然后确定哪个节点当前托管该分片的领导者。然后将文档转发给当前领导者进行索引,领导者将更新转发给所有其他副本。
副本类型
默认情况下,如果其领导者宕机,所有副本都有资格成为领导者。但是,这需要付出代价:如果所有副本随时都可以成为领导者,则每个副本都必须始终与其领导者保持同步。添加到领导者的新文档必须路由到副本,并且每个副本都必须提交。如果副本宕机或暂时不可用,然后重新加入集群,如果它错过了大量更新,则恢复可能会很慢。
对于大多数用户来说,这些问题不是问题。但是,如果副本的行为更像以前模型,则某些用例会表现得更好,无论是不实时同步还是根本没有资格成为领导者。
Solr 通过允许你在创建新集合或添加副本时设置副本类型来实现这一点。可用的类型有
-
NRT:这是默认值。NRT 副本(NRT = NearRealTime)维护一个事务日志,并将新文档本地写入其索引。此类型的任何副本都有资格成为领导者。传统上,这是 Solr 支持的唯一类型。
-
TLOG:此类型的副本维护一个事务日志,但不会在本地索引文档更改。由于副本中不需要发生提交,因此此类型有助于加快索引速度。当此类型的副本需要更新其索引时,它会通过从领导者复制索引来执行此操作。此类型的副本也有资格成为分片领导者;它将首先处理其事务日志来执行此操作。如果它确实成为领导者,则其行为将与 NRT 类型的副本相同。
-
PULL:此类型的副本不维护事务日志,也不在本地索引文档更改。它只从分片领导者复制索引。它没有资格成为分片领导者,并且根本不参与分片领导者选举。
如果你在创建副本时未指定副本类型,则它将为 NRT 类型。
使用 PULL 副本进行恢复
如果 PULL 副本宕机或离开集群,则需要考虑一些场景。
如果 PULL 副本无法同步到领导者,因为领导者已宕机,则不会发生复制。但是,它将继续提供查询。一旦它可以再次连接到领导者,复制将恢复。
如果 PULL 副本无法连接到 ZooKeeper,它将从集群中移除,并且集群不会向它路由查询。
如果 PULL 副本死亡或由于任何其他原因而无法访问,则无法对其进行查询。当它重新加入集群时,它将从领导者复制,并且完成后,它将再次准备好提供查询。
具有首选副本类型的查询
默认情况下,所有副本都提供查询。有关如何为查询指示首选副本类型的详细信息,请参阅部分 shards.preference 参数。
文档路由
Solr 提供了通过在 创建集合 时指定 router.name
参数来指定集合使用的路由器实现的功能。
如果您使用 compositeId
路由器(默认),则可以发送带有文档 ID 中前缀的文档,该前缀将用于计算哈希值,Solr 使用该哈希值来确定将文档发送到哪个分片进行索引。前缀可以是您希望的任何内容(例如,它不必是分片名称),但它必须一致,以便 Solr 行为一致。
例如,如果您想为某个客户共同定位文档,则可以使用客户名称或 ID 作为前缀。例如,如果您的客户是“IBM”,文档 ID 为“12345”,则可以将前缀插入到文档 ID 字段中:“IBM!12345”。此处的感叹号('!')至关重要,因为它区分了用于确定将文档定向到哪个分片的前缀。
然后在查询时间,您可以使用 _route_
参数(即 q=solr&_route_=IBM!
)将前缀包含到您的查询中,以将查询定向到特定分片。在某些情况下,这可能会提高查询性能,因为它克服了查询所有分片时的网络延迟。
compositeId
路由器支持包含最多 2 级路由的前缀。例如:首先按区域路由,然后按客户路由的前缀:“USA!IBM!12345”
另一个用例可能是如果客户“IBM”有大量文档,并且您希望将其分散到多个分片中。此类用例的语法为:shard_key/num!document_id
,其中 /num
是复合哈希中要使用的分片键中的位数。
因此,IBM/3!12345
将从分片键中获取 3 位,从唯一文档 ID 中获取 29 位,将租户分散到集合中 1/8 的分片中。同样,如果 num 值为 2,它将使文档分散到 1/4 的分片数量中。在查询时间,您可以使用 _route_
参数(即 q=solr&_route_=IBM/3!
)将前缀以及位数包含到您的查询中,以将查询定向到特定分片。
如果您不想影响文档的存储方式,则无需在文档 ID 中指定前缀。
如果您创建了集合并在创建时定义了“隐式”路由器,则还可以定义一个 router.field
参数,以使用每个文档中的一个字段来标识文档所属的分片。但是,如果文档中缺少指定字段,则文档将被拒绝。您还可以使用 _route_
参数来命名特定分片。
分片拆分
在 SolrCloud 中创建集合时,您需要决定要使用的初始分片数。但很难提前知道您需要的分片数,尤其是当组织要求可能随时发生变化时,并且稍后发现您选择错误的代价可能很高,包括创建新核心和重新索引所有数据。
分片拆分功能在 Collections API 中。它目前允许将分片拆分为两部分。现有分片保持原样,因此拆分操作实际上将数据作为新分片制作两份副本。您可以在准备好后稍后删除旧分片。
有关如何使用分片拆分的更多详细信息,请参阅有关 Collection API 的 SPLITSHARD 命令的部分。
忽略 SolrCloud 中客户端应用程序的提交
在大多数情况下,在 SolrCloud 模式下运行时,索引客户端应用程序不应发送显式提交请求。相反,您应该使用 openSearcher=false
和 autoSoftCommit
配置自动提交,以使最近的更新在搜索请求中可见。这确保了在集群中按常规计划进行自动提交。
使用 autoSoftCommit 或 commitWithin 要求客户端应用程序接受“最终一致性”的现实。Solr 将在集合的副本中大致同时使文档可搜索,但没有严格的保证。因此,在极少数情况下,文档可能出现在一个搜索中,但不会出现在紧随其后的后续搜索中,当第二个搜索路由到不同的副本时。此外,当存在分片时,按特定顺序添加的文档(即使在同一批次中)可能会在提交顺序之外变得可搜索。在下一个 autoCommit 或 commitWithin 间隔到期后,文档将在分片的所有副本上可见。 |
要强制执行客户端应用程序不应发送显式提交的策略,您应更新将数据编入索引到 SolrCloud 的所有客户端应用程序。但是,这并不总是可行的,因此 Solr 提供了 IgnoreCommitOptimizeUpdateProcessorFactory
,它允许您忽略客户端应用程序的显式提交和/或优化请求,而无需重构您的客户端应用程序代码。
要激活此请求处理器,您需要将以下内容添加到您的 solrconfig.xml
<updateRequestProcessorChain name="ignore-commit-from-client" default="true">
<processor class="solr.IgnoreCommitOptimizeUpdateProcessorFactory">
<int name="statusCode">200</int>
</processor>
<processor class="solr.LogUpdateProcessorFactory" />
<processor class="solr.DistributedUpdateProcessorFactory" />
<processor class="solr.RunUpdateProcessorFactory" />
</updateRequestProcessorChain>
如上例所示,处理器将向客户端返回 200,但将忽略提交或优化请求。请注意,您还需要连接 SolrCloud 所需的隐式处理器,因为此自定义链取代了默认链。
在以下示例中,处理器将引发带有 403 代码和自定义错误消息的异常
<updateRequestProcessorChain name="ignore-commit-from-client" default="true">
<processor class="solr.IgnoreCommitOptimizeUpdateProcessorFactory">
<int name="statusCode">403</int>
<str name="responseMessage">Thou shall not issue a commit!</str>
</processor>
<processor class="solr.LogUpdateProcessorFactory" />
<processor class="solr.DistributedUpdateProcessorFactory" />
<processor class="solr.RunUpdateProcessorFactory" />
</updateRequestProcessorChain>
最后,您还可以通过执行以下操作将其配置为仅忽略优化并让提交通过
<updateRequestProcessorChain name="ignore-optimize-only-from-client-403">
<processor class="solr.IgnoreCommitOptimizeUpdateProcessorFactory">
<str name="responseMessage">Thou shall not issue an optimize, but commits are OK!</str>
<bool name="ignoreOptimizeOnly">true</bool>
</processor>
<processor class="solr.RunUpdateProcessorFactory" />
</updateRequestProcessorChain>