SolrCloud 分片與索引

當您的集合對於單一節點來說太大時,您可以透過建立多個分片來將其分割並儲存於各區段。

分片是集合的邏輯分割區,其中包含集合中文件的子集,因此集合中的每個文件都只包含在一個分片中。哪個分片包含集合中的每個文件取決於該集合的整體分片策略。

例如,您可能有一個集合,其中每個文件的「國家」欄位決定它屬於哪個分片,因此來自同一個國家的文件會位於相同位置。不同的集合可能只是使用每個文件 uniqueKey 的「雜湊」來決定其分片。

在 SolrCloud 之前,Solr 支援分散式搜尋,它允許在多個分片中執行一個查詢,因此查詢會針對整個 Solr 索引執行,且搜尋結果中不會遺漏任何文件。因此,跨分片分割索引並非 SolrCloud 獨有的概念。然而,分散式方法存在一些問題,需要透過 SolrCloud 進行改進

  1. 將索引分割成分片有些人工操作。

  2. 沒有支援分散式索引,這表示您需要明確地將文件傳送到特定的分片;Solr 無法自行判斷要將文件傳送到哪個分片。

  3. 沒有負載平衡或容錯移轉,因此如果您收到大量的查詢,您需要找出要將它們傳送到哪裡,並且如果一個分片失效,它就消失了。

SolrCloud 解決了這些限制。它支援自動分配索引程序和查詢,並且 ZooKeeper 提供容錯移轉和負載平衡。此外,每個分片可以有多個副本以增加可靠性。

領導者與副本

在 SolrCloud 中,沒有領導者或追隨者。相反地,每個分片都包含至少一個實體副本,其中恰好一個是領導者。領導者會自動選出,最初是先到先得,然後根據 https://zookeeper.dev.org.tw/doc/r3.9.2/recipes.html#sc_leaderElection 中描述的 ZooKeeper 程序選出。

如果領導者失效,其他副本之一會自動選為新的領導者。

當文件傳送到 Solr 節點進行索引時,系統會先判斷該文件屬於哪個分片,然後判斷哪個節點目前正在託管該分片的領導者。然後將文件轉送到目前的領導者進行索引,而領導者會將更新轉送到所有其他副本。

副本類型

預設情況下,如果領導者失效,所有副本都有資格成為領導者。但是,這是有代價的:如果所有副本都可以在任何時候成為領導者,則每個副本都必須隨時與其領導者同步。新增至領導者的新文件必須路由到副本,並且每個副本都必須執行提交。如果副本失效或暫時無法使用,然後重新加入叢集,如果它遺失了大量的更新,復原可能會很慢。

這些問題對於大多數使用者來說不是問題。但是,某些使用案例如果副本的行為更像以前的模型(例如,不即時同步或完全沒有資格成為領導者),則效能會更好。

Solr 允許您在建立新集合或新增副本時設定副本類型,藉此解決這個問題。可用的類型如下

  • NRT:這是預設值。NRT 副本 (NRT = 近即時) 會維護交易日誌,並將新的文件寫入其本機索引。任何此類型的副本都有資格成為領導者。傳統上,這是 Solr 唯一支援的類型。

  • TLOG:此類型的副本會維護交易日誌,但不會在本機為文件變更建立索引。此類型有助於加速索引建立,因為副本中不需要執行提交作業。當此類型的副本需要更新其索引時,它會從領導者複製索引。此類型的副本也有資格成為分片領導者;它會先處理其交易日誌來執行此操作。如果它確實成為領導者,其行為將與 NRT 類型的副本相同。

  • PULL:此類型的副本不會維護交易日誌,也不會在本地為文件變更建立索引。它只會從分片領導者複製索引。它沒有資格成為分片領導者,並且根本不參與分片領導者選舉。

如果您在建立副本時未指定副本類型,則會預設為 NRT 類型。

在叢集中組合副本類型

建議使用三種副本類型的組合

  • 所有 NRT 副本

  • 所有 TLOG 副本

  • TLOG 副本與 PULL 副本

所有 NRT 副本

適用於中小型叢集,甚至更新(索引)吞吐量不太高的大型叢集。NRT 是唯一支援軟提交的副本類型,因此當需要近即時性時,也請使用此組合。

所有 TLOG 副本

如果不需要近即時性,且每個分片的副本數量很高,但您仍然希望所有副本都能處理更新請求,請使用此組合。

TLOG 副本加上 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 中指定前綴。

如果您在建立集合時定義了「implicit」路由器,則可以另外定義 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>