SolrCloud 分片和索引

如果你的集合对于一个节点来说太大,你可以通过创建多个分片将其分解并按部分存储。

分片是集合的逻辑分区,其中包含集合中部分文档,以便集合中的每个文档都恰好包含在一个分片中。集合中的每个文档包含在哪个分片中取决于该集合的整体分片策略。

例如,你可能有一个集合,其中每个文档的“国家/地区”字段决定了它属于哪个分片,以便来自同一国家/地区的文档共置。另一个集合可能只是对每个文档的 uniqueKey 使用“哈希”来确定其分片。

在 SolrCloud 之前,Solr 支持分布式搜索,它允许在多个分片上执行一个查询,以便针对整个 Solr 索引执行查询,并且不会从搜索结果中遗漏任何文档。因此,跨分片拆分索引并不是一个专门的 SolrCloud 概念。但是,分布式方法存在几个问题,需要通过 SolrCloud 进行改进

  1. 将索引拆分成分片有点手动。

  2. 不支持分布式索引,这意味着你需要明确地将文档发送到特定分片;Solr 无法自行确定将文档发送到哪些分片。

  3. 没有负载平衡或故障转移,因此,如果你收到大量查询,你需要确定将它们发送到哪里,如果一个分片死了,它就消失了。

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 类型。

在集群中组合副本类型

建议使用三种副本类型组合

  • 所有 NRT 副本

  • 所有 TLOG 副本

  • TLOG 副本和 PULL 副本

所有 NRT 副本

对于中小型集群,甚至更新(索引)吞吐量不太高的较大集群,请使用此组合。NRT 是唯一支持软提交的副本类型,因此在需要 NearRealTime 时也使用此组合。

所有 TLOG 副本

如果不需要 NearRealTime,并且每个分片的副本数量很高,但你仍希望所有副本都能处理更新请求,请使用此组合。

TLOG 副本和 PULL 副本

如果不需要 NearRealTime,每个分片的副本数量很高,并且你希望提高对文档更新的搜索查询的可用性,即使这意味着暂时提供过时的结果,请使用此组合。

其他副本类型组合

不建议使用其他副本类型组合。如果分片中有多个副本正在写入自己的索引,而不是从 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=falseautoSoftCommit 配置自动提交,以使最近的更新在搜索请求中可见。这确保了在集群中按常规计划进行自动提交。

使用 autoSoftCommitcommitWithin 要求客户端应用程序接受“最终一致性”的现实。Solr 将在集合的副本中大致同时使文档可搜索,但没有严格的保证。因此,在极少数情况下,文档可能出现在一个搜索中,但不会出现在紧随其后的后续搜索中,当第二个搜索路由到不同的副本时。此外,当存在分片时,按特定顺序添加的文档(即使在同一批次中)可能会在提交顺序之外变得可搜索。在下一个 autoCommitcommitWithin 间隔到期后,文档将在分片的所有副本上可见。

要强制执行客户端应用程序不应发送显式提交的策略,您应更新将数据编入索引到 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>