SolrCloudシャードとインデックス作成

コレクションが1つのノードに対して大きすぎる場合、複数個の**シャード**を作成することで、コレクションを分割して保存できます。

シャードはコレクションの論理的なパーティションであり、コレクション内のドキュメントのサブセットを含んでいます。コレクション内のすべてのドキュメントは、正確に1つのシャードに含まれます。コレクションの各ドキュメントがどのシャードに含まれるかは、そのコレクション全体のシャード化戦略によって異なります。

たとえば、「country」フィールドが各ドキュメントの所属するシャードを決定するコレクションがあります。そのため、同じ国のドキュメントは同じ場所に配置されます。別のコレクションでは、各ドキュメントのuniqueKeyの「ハッシュ」を使用してシャードを決定する場合もあります。

SolrCloud以前は、Solrは分散検索をサポートしていました。これにより、1つのクエリを複数のシャードで実行でき、クエリはSolrインデックス全体に対して実行され、検索結果からドキュメントが欠落することはありませんでした。したがって、インデックスをシャードに分割することは、SolrCloud固有の概念ではありません。しかし、分散アプローチにはいくつかの問題があり、SolrCloudによる改善が必要でした。

  1. インデックスをシャードに分割する作業は、ある程度手動で行う必要がありました。

  2. 分散インデックス作成はサポートされていませんでした。つまり、ドキュメントを特定のシャードに明示的に送信する必要があり、Solrがドキュメントを送信するシャードを独自に判断することはできませんでした。

  3. ロードバランシングやフェイルオーバーはありませんでした。そのため、クエリ数が多くなった場合、クエリを送信する場所を判断する必要があり、1つのシャードがダウンすると、そのシャードは完全に使用できなくなりました。

SolrCloudはこれらの制限に対処します。インデックスプロセスとクエリの両方を自動的に分散するためのサポートがあり、ZooKeeperはフェイルオーバーとロードバランシングを提供します。さらに、各シャードには、堅牢性を高めるために複数のレプリカを持つことができます。

リーダーとレプリカ

SolrCloudには、リーダーとフォロワーはありません。代わりに、各シャードは少なくとも1つの物理的な**レプリカ**で構成され、そのうち正確に1つが**リーダー**です。リーダーは自動的に選出され、最初は先着順に、その後https://zookeeper.dokyumento.jp/doc/r3.9.1/recipes.html#sc_leaderElectionに記載されているZooKeeperプロセスに基づいて選出されます。

リーダーがダウンした場合、他のレプリカの1つが自動的に新しいリーダーとして選出されます。

ドキュメントがインデックス作成のためにSolrノードに送信されると、システムはまずそのドキュメントが属するシャードを決定し、次にそのシャードのリーダーを現在ホストしているノードを決定します。その後、ドキュメントはインデックス作成のために現在のリーダーに転送され、リーダーは更新を他のすべてのレプリカに転送します。

レプリカの種類

デフォルトでは、すべてのレプリカは、リーダーがダウンした場合にリーダーになることができます。ただし、これにはコストがかかります。すべてのレプリカがいつでもリーダーになれる場合、すべてのレプリカは常にリーダーと同期している必要があります。リーダーに追加された新しいドキュメントはレプリカにルーティングする必要があり、各レプリカはコミットを実行する必要があります。レプリカがダウンしたり、一時的に利用できなくなったりしてからクラスタに再参加した場合、多数の更新を見逃している場合は、復旧が遅くなる可能性があります。

これらの問題は、ほとんどのユーザーにとって問題ではありません。ただし、レプリカがリアルタイムで同期しない、またはリーダーになることができないなどの、以前のモデルのように動作する場合、一部のユースケースのパフォーマンスが向上します。

Solrは、新しいコレクションを作成する際、またはレプリカを追加する際にレプリカの種類を設定できるようにすることで、これを実現します。使用可能な種類は次のとおりです。

  • NRT:これがデフォルトです。NRTレプリカ(NRT = NearRealTime)はトランザクションログを維持し、新しいドキュメントをローカルのインデックスに書き込みます。このタイプのレプリカはすべて、リーダーになることができます。従来、これはSolrでサポートされている唯一のタイプでした。

  • TLOG:このタイプのレプリカはトランザクションログを維持しますが、ドキュメントの変更をローカルにインデックス付けしません。このタイプは、レプリカでコミットを実行する必要がないため、インデックス作成の高速化に役立ちます。このタイプのレプリカがインデックスを更新する必要がある場合、リーダーからインデックスをレプリケートすることによって行われます。このタイプのレプリカもシャードリーダーになることができます。リーダーになるには、最初にトランザクションログを処理します。リーダーになった場合、NRTタイプのレプリカと同じように動作します。

  • PULL:このタイプのレプリカは、トランザクションログを維持せず、ドキュメントの変更をローカルにインデックス付けしません。シャードリーダーからインデックスをレプリケートするだけです。シャードリーダーになることはできず、シャードリーダーの選出にはまったく参加しません。

レプリカの作成時にレプリカの種類を指定しない場合、NRTタイプになります。

クラスタでのレプリカタイプの組み合わせ

推奨されるレプリカタイプの組み合わせは3つあります。

  • すべての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に含まれています。現在、シャードを2つの部分に分割できます。既存のシャードはそのまま残されるため、分割アクションにより、データの2つのコピーが新しいシャードとして事実上作成されます。準備ができたら、後で古いシャードを削除できます。

シャード分割の使用方法の詳細については、Collection APIのSPLITSHARDコマンドに関するセクションを参照してください。

SolrCloudでのクライアントアプリケーションからのコミットの無視

ほとんどの場合、SolrCloudモードで実行しているときは、インデックス作成クライアントアプリケーションは明示的なコミット要求を送信するべきではありません。代わりに、openSearcher=falseおよびautoSoftCommitを使用して自動コミットを構成し、最近の更新を検索要求で表示できるようにする必要があります。これにより、クラスタで定期的に自動コミットが行われます。

autoSoftCommitまたはcommitWithinを使用するには、クライアントアプリケーションが「最終的な整合性」の現実を受け入れる必要があります。Solrは、コレクションのレプリカ全体でほぼ同時にドキュメントを検索可能にしますが、厳密な保証はありません。その結果、まれに、1つの検索には表示されるドキュメントが、2番目の検索が別のレプリカにルーティングされた場合、最初の検索の直後に発生する後続の検索には表示されない可能性があります。また、特定の順序(同じバッチ内でも)で追加されたドキュメントは、シャードがある場合、送信順序とは異なる順序で検索可能になる場合があります。ドキュメントは、次の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>