AEM 6 での Oak への移行に伴い、クエリとインデックスの管理方法に関して大きな変更がいくつか導入されました。Jackrabbit 2 では、すべてのコンテンツはデフォルトでインデックス付けされ、自由に照会できます。 Oak では、oak:index
ノードの下にインデックスを手動で作成する必要があります。クエリはインデックスなしで実行できますが、大きなデータセットの場合は実行に時間がかかり、中止することもできます。
この記事では、インデックスを作成するタイミングと不要な場合の概要、クエリが不要な場合の使用を回避するためのテクニック、可能な限り最適に実行するためのインデックスとクエリの最適化に関するヒントについて説明します。
また、必ず クエリとインデックスの作成に関する Oak ドキュメント. AEM 6 における新しい概念であるインデックスに加えて、Oak クエリの構文も大きく異なります。以前の AEM インストールからコードを移行する際にはこの違いを考慮する必要があります。
リポジトリの分類を設計する際は、いくつかの要因を考慮する必要があります。これには、アクセス制御、ローカライゼーション、コンポーネント、ページプロパティの継承などが含まれます。
こうした事柄に対応する分類を設計する一方で、インデックス設計の「トラバーサビリティ」についても検討することも重要です。トラバーサビリティとは、この文脈では、パスに基づいて予想どおりにコンテンツにアクセスできるようにする分類の機能を言います。これにより、多数のクエリを実行する場合よりも保守が容易な、よりパフォーマンスの高いシステムが実現します。
また、分類を設計する際は、順序が重要かどうかを考慮することが重要です。 明示的な順序が不要で、多くの兄弟ノードが想定される場合は、次のような順序なしのノードタイプを使用することをお勧めします。 sling:Folder
または oak:Unstructured
. 順序が必要な場合、 nt:unstructured
、および sling:OrderedFolder
の方が適切です。
AEMシステムで実行されるクエリは、より負担のかかる操作の 1 つになる可能性があるので、コンポーネント内でクエリを使用しないことをお勧めします。 ページがレンダリングされるたびに複数のクエリを実行すると、システムのパフォーマンス低下につながります。コンポーネントのレンダリング時にクエリが実行されることを回避するには、ノードの走査と結果の先取りという 2 つの方法があります。
必要なデータの場所を事前に把握できるようにリポジトリを設計した場合、必要なパスからこのデータを取得するコードをデプロイできます。クエリを実行して検索する必要はありません。
例えば、特定のカテゴリに適合するコンテンツのレンダリングが行われます。1 つの方法として、コンテンツをカテゴリプロパティで整理し、クエリを実行して、カテゴリ内の項目を表示するコンポーネントに入力できるようにします。
より優れた方法は、このコンテンツを手動で取得できるように、カテゴリ別の分類に構造化することです。
例えば、コンテンツが次のような分類に格納されている場合です。
/content/myUnstructuredContent/parentCategory/childCategory/contentPiece
The /content/myUnstructuredContent/parentCategory/childCategory
ノードは単に取得でき、その子を解析してコンポーネントのレンダリングに使用できます。
また、小さな結果セットや同種の結果セットを扱う場合は、同じ結果セットを返すクエリを作成するよりも、リポジトリを経由して必要なノードを収集する方が速くなります。 一般的な考慮事項として、クエリはできる限り使用せずに済ませる必要があります。
コンテンツやコンポーネントの要件によっては、必要なデータを取得する方法としてノードの走査を使用できない場合があります。その場合は、エンドユーザーに最適なパフォーマンスを保証するために、コンポーネントがレンダリングされる前に必要なクエリを実行する必要があります。
コンポーネントで必要とされる結果をオーサリング時にまとめて計算でき、さらにコンテンツがその後も変更されないとわかっている場合は、オーサーがダイアログで設定を適用するときにクエリを実行できます。
データまたはコンポーネントが定期的に変更される場合は、スケジュールに従って、または基礎データの更新リスナーを使用してクエリを実行できます。その後、結果をリポジトリ内の共有場所に書き込むことができます。このデータを必要とするコンポーネントは、実行時にクエリを実行しなくても、この 1 つのノードから値を取り出すことが可能です。
インデックスを使用していないクエリを実行すると、ノードトラバーサルに関する警告が記録されます。 このクエリが頻繁に実行されるクエリの場合は、インデックスを作成します。 特定のクエリが使用しているインデックスを確認するには、クエリの説明を実行ツールを推奨します。さらに情報を得るために、関連する検索 API の DEBUG ログを有効にすることもできます。
インデックス定義を変更した後、インデックスを再構築(再インデックス)する必要があります。 インデックスのサイズによっては、この処理を終えるまでにある程度時間がかかることがあります。
複雑なクエリを実行するときは、クエリを複数の小さなクエリに分割し、後からコードによってデータを結合するほうが、パフォーマンスが向上することがあります。その場合のレコメンデーションは、2 つの方法のパフォーマンスを比較し、当該のユースケースでどちらの選択肢が優れているか確認することです。
AEM では、次の 3 つの方法でクエリを記述できます。
すべてのクエリは実行前に SQL2 に変換されますが、クエリ変換のオーバーヘッドはごくわずかなので、クエリ言語を選択するときの主な懸念事項は可読性と開発チームの安心感ということになります。
QueryBuilder を使用する場合、デフォルトで結果数を決定します。Oak では、以前のバージョンの Jackrabbit に比べて時間がかかります。 これを補うために、guessTotal パラメーターを使用できます。
任意のクエリ言語と同様に、クエリを最適化する最初の手順は、クエリの実行方法を理解することです。 これをおこなうには、操作ダッシュボードにあるクエリの説明を実行ツールを使用します。このツールを使用すると、クエリにプラグインして説明を取得できます。クエリが大きなリポジトリと実行時に問題を引き起こす場合、および使用されているインデックスに関する警告が表示されます。 このツールでは、低速なクエリやよく使用されるクエリのリストを読み込み、これらを説明および最適化できます。
Oak による使用するインデックスの選択方法と、クエリエンジンによるクエリの実際の実行方法に関する追加情報を得るために、DEBUG ログ設定を以下のパッケージに追加できます。
クエリのデバッグが完了したら、必ずこのロガーを削除してください。 大量のアクティビティを出力する傾向があり、最終的にはログファイルでディスクがいっぱいになる場合があります。
この方法について詳しくは、ログに関するドキュメントを参照してください。
Lucene では、インデックス付きのコンテンツに関する詳細(各インデックス内に存在するドキュメントのサイズと数を含む)を提供する JMX Bean が登録されます。
これを確認するには、JMX コンソール(https://server:port/system/console/jmx
)にアクセスしてください。
JMX コンソールにログインしたら、以下を検索します。 Lucene インデックスの統計 見つけるために 他のインデックス統計は、IndexStats MBean にあります。
クエリ統計の場合は、 Oak クエリ統計.
Luke などのツールを使用してインデックスを詳しく調べる場合は、Oak コンソールを使用して NodeStore
のインデックスをファイルシステムディレクトリにダンプする必要があります。この方法については、 Lucene ドキュメント.
また、JSON 形式でシステム内のインデックスを抽出できます。これをおこなうには、次にアクセスする必要があります: https://server:port/oak:index.tidy.-1.json
開発中
の低しきい値の設定 oak.queryLimitInMemory
( 例:10000) と Oak。 queryLimitReads
(例えば、5000)を使用し、UnsupportedOperationException をヒットした場合に「The query read more than x nodes…」というメッセージを表示し、高価なクエリを最適化します。
これにより、リソースを大量に消費するクエリ(つまり、インデックスでバックアップされていないクエリや、インデックスのカバーが少ないクエリ)を回避できます。 例えば、100 万個のノードを読み取るクエリでは I/O が増加し、アプリケーションの全体的なパフォーマンスに悪影響が生じます。上述のような制限が原因で失敗するクエリは、分析して最適化する必要があります。
ログを監視して、大規模なノードの走査やヒープメモリの大量使用を引き起こしているクエリがないかどうかを調べます。
*WARN* ... java.lang.UnsupportedOperationException: The query read or traversed more than 100000 nodes. To avoid affecting other tasks, processing was stopped.
大量のヒープメモリ消費をトリガーするクエリがないか、ログを監視します。
*WARN* ... java.lang.UnsupportedOperationException: The query read more than 500000 nodes in memory. To avoid running out of memory, processing was stopped
AEM 6.0 ~ 6.2 では、AEM 起動スクリプトの JVM パラメーターを使用してノードのトラバーサルのしきい値を調整し、大きなクエリによる環境への過負荷を防ぐことができます。
推奨される値は次のとおりです。
-Doak.queryLimitInMemory=500000
-Doak.queryLimitReads=100000
AEM 6.3 では、上記の 2 つのパラメーターは事前に設定されており、OSGi QueryEngineSettings を使用して保持できます。
詳しくは、https://jackrabbit.apache.org/oak/docs/query/query-engine.html#Slow_Queries_and_Read_Limitsを参照してください。
インデックスを作成または最適化する際に最初に尋ねる質問は、特定の状況でインデックスが必要かどうかです。 そのクエリを一度またはごくまれにしか実行せず、システムのオフピーク時にバッチ処理で実行する場合は、インデックスを作成しないほうが良い場合があります。
インデックスを作成すると、そのインデックスが付けられたデータを更新するたびに、インデックスも更新する必要が生じます。これはシステムのパフォーマンスに影響を与えるので、インデックスを作成する必要があるのは、必要な場合のみにしてください。
また、インデックスは、インデックス内に含まれるデータが十分に一意で、それを保証できる場合にのみ役立ちます。 ある本のインデックス(索引)と、本が取り上げるトピックを考えてみましょう。テキスト内の一連のトピックにインデックスを作成する場合、通常は数百から数千のエントリが存在します。これにより、ページのサブセットにすばやくジャンプして、探している情報をすばやく見つけることができます。 そのインデックスに 2、3 個のエントリしかなく、それぞれが数百ページを指す場合、インデックスは役に立ちません。 この同じ概念は、データベースインデックスにも当てはまります。一意の値が 2 つだけある場合、インデックスは役に立ちません。 ただし、インデックスが大きすぎて役に立たない場合もあります。 インデックス統計を見るには、上記のインデックス統計を参照してください。
Lucene インデックスは Oak 1.0.9 で導入され、AEM 6 の初回起動時に導入されたプロパティインデックスに対して、強力な最適化を提供します。Lucene インデックスまたはプロパティインデックスのどちらを使用するかを決定する際は、次の点を考慮してください。
一般に、プロパティインデックスを使用する必要性がない限り、より高いパフォーマンスと柔軟性のメリットを得るために Lucene インデックスを使用することをお勧めします。
AEMは、デフォルトで Solr のインデックス作成もサポートしています。 これは全文検索をサポートするために使用されますが、任意の種類の JCR クエリをサポートするために使用することもできます。 AEM インスタンスに、検索に負荷がかかるデプロイメント(多数の同時ユーザーを持つ検索主導型 web サイトなど)で必要なクエリの数を処理する CPU 処理能力がない場合は、Solr を検討する必要があります。または、Solr をクローラーベースのアプローチで実装して、プラットフォームのより高度な機能の一部を使用することもできます。
Solr インデックスは、AEM サーバー上で開発環境用に埋め込んで実行するように設定することも、リモートインスタンスにオフロードして、実稼動環境とステージング環境での検索のスケーラビリティを向上させることもできます。オフロード検索を使用するとスケーラビリティが向上しますが、遅延が生じるので、必要な場合を除きお勧めしません。 Solr 統合の設定方法と Solr インデックスの作成方法について詳しくは、Oak クエリとインデックス作成に関するドキュメントを参照してください。
統合型の Solr 検索アプローチを採用すると、インデックス作成を Solr サーバーにオフロードできます。Solr サーバーのより高度な機能をクローラーベースのアプローチで使用する場合は、追加の設定作業が必要です。
このアプローチの欠点は、デフォルトではAEMクエリが ACL に従うので、ユーザーがアクセスできない結果は非表示になり、Solr サーバーに対する検索を外部化してもこの機能はサポートされないということです。 この方法で検索をオフロードする場合は、ユーザーに見せてはいけない結果を表示していないかを特に注意して確認する必要があります。
このアプローチが適しているユースケースとしては、検索データを複数のソースから集めなければならない場合が考えられます。例えば、AEM上でホストされているサイトと、サードパーティプラットフォーム上でホストされている 2 つ目のサイトがあるとします。 この場合は、両方のサイトのコンテンツをクロールし、その結果を統合インデックスに格納するよう Solr を設定できます。こうすることで、クロスサイト検索が可能になります。
Lucene インデックスに関する Oak のドキュメントでは、以下のとおり、インデックス設計時の考慮事項をいくつか挙げています。
クエリで異なるパス制限が使用される場合は、 evaluatePathRestrictions
. これにより、クエリは、指定されたパスの下の結果のサブセットを返し、クエリに基づいて結果をフィルタリングできます。 それ以外の場合は、クエリはリポジトリ内のクエリパラメーターに一致するすべての結果を検索し、パスに基づいてフィルタリングします。
クエリで並べ替えを使用する場合は、並べ替えるプロパティの明示的なプロパティ定義を用意し、そのプロパティの ordered
を true
に設定します。これにより、結果をインデックス内と同じように並べ替え、クエリ実行時のコストの高い並べ替え操作を節約できます。
インデックスには必要なものだけを追加します。不要な機能やプロパティを追加すると、インデックスが増え、パフォーマンスが低下します。
プロパティインデックスでは、一意のプロパティ名を使用すると、インデックスのサイズを削減できます。しかし Lucene インデックスでは、統一されたインデックスを実現するために、nodeTypes
と mixins
を使用する必要があります。特定の nodeType
または mixin
をクエリすると、nt:base
をクエリするよりもパフォーマンスが向上します。この方法を使用する場合は、当該の nodeTypes
の indexRules
を定義します。
クエリを特定のパスでのみ実行する場合は、そのパスの下でインデックスを作成します。インデックスにリポジトリのルートを含める必要はありません。
インデックスを作成するすべてのプロパティが Lucene で可能な限り多くのプロパティ制限をネイティブに評価できるように関連付けられている場合は、1 つのインデックスを使用します。 また、結合を実行する場合でも、1 つのクエリで 1 つのインデックスのみが使用されます。
NodeStore
がリモートに格納されている場合は、CopyOnRead
というオプションを有効にできます。このオプションを指定すると、リモートインデックスが読み込まれたときに、リモートインデックスがローカルファイルシステムに書き込まれます。 このオプションは、こうしたリモートインデックスに対して頻繁に実行されるクエリのパフォーマンス向上に役立ちます。
これは OSGi コンソールの LuceneIndexProvider サービスで設定でき、Oak 1.0.13 の時点ではデフォルトで有効です。
インデックスを削除するときは、必ず type
プロパティを disabled
に設定して一時的にインデックスを無効にしたうえで、実際に削除する前にテストを実施し、アプリケーションが正しく動作するか確認することを推奨します。インデックスは無効の間は更新されないので、再度有効にした場合、インデックスを再作成する必要がある場合は、正しいコンテンツが含まれていない可能性があります。
TarMK インスタンスでプロパティインデックスを削除した後、使用中のディスク領域を再利用するには、コンパクションを実行する必要があります。 Lucene インデックスの場合、実際のインデックスコンテンツは BlobStore に格納されているので、データストアのガベージコレクションが必要になります。
MongoDB インスタンスのインデックスを削除する場合、削除のコストはインデックスのノードの数に比例します。大量のインデックスを削除すると問題が生じることがあるので、インデックスを無効にしてから、メンテナンスウィンドウ中に oak-mongo.js などのツールを使用して削除する方法が推奨されます。データの不整合が生じる可能性があるので、この方法は通常のノードコンテンツには使用しないでください。
oak-mongo.js について詳しくは、Oak のドキュメントの Command Line Tools のセクションを参照してください。
効率的な JCR クエリとインデックス定義を作成できるようにするため、開発時に JCR クエリチートシートをダウンロードして参照用として使用できます。これには QueryBuilder、XPath、SQL-2 のクエリの例が収録されていて、クエリのパフォーマンスの点で異なる動作をする複数のシナリオに対応できます。また、Oak インデックスの作成またはカスタマイズ方法に関するレコメンデーションも収録されています。このチートシートの内容は、AEM 6.5 および AEM as a Cloud Service に適用されます。
この節では、 のみ Oak インデックスを再インデックスする受け入れ可能な理由。
以下に説明する理由の外、Oak インデックスの再インデックスを開始すると次のことがおこなわれます not 動作を変更したり問題を解決したりすると、AEM上の負荷が不必要に増加します。
以下の表に示す理由でカバーされない限り、Oak インデックスの再インデックスは避ける必要があります。
以下の表を参照して、インデックスの再作成が有用かどうかを判断する前に、 常に 確認:
Oak インデックスの再インデックスに対して許容できる誤りのない条件は、Oak インデックスの設定が変更された場合のみです。
インデックスの再作成は、AEM全体のパフォーマンスに与える影響を常に適切に考慮して取り組み、アクティビティが低い時間帯やメンテナンス時間帯に実行する必要があります。
以下の節では、発生する可能性がある問題と解決策について詳しく説明します。
適用対象:
症状:
検証方法:
jcr:created
またはjcr:lastModified
プロパティをインデックスの変更時間と照合して検証します。解決方法:
再インデックス lucene 指数
または、見つからないノードについて処理(安全な書き込み操作)を行います。
適用対象:
症状:
検証方法:
diffStoredIndexDefinition
.解決方法:
次の表に、Oak インデックスの再インデックスによって問題が解決される、許容できるエラーと例外的な状況を示します。
以下に示す条件に一致しないAEMで問題が発生した場合は、 not 問題が解決しないので、インデックスを再インデックスします。
以下の節では、発生する可能性がある問題と解決策について詳しく説明します。
適用対象:
症状:
検証方法:
解決方法:
リポジトリ走査チェックを実行します。例:
http://localhost:4502/system/console/repositorycheck
リポジトリの走査により(lucene ファイル以外の)他のバイナリが欠落していないか判断
Lucene インデックス以外のバイナリが見つからない場合は、バックアップから復元します。
それ以外の場合は、 再インデックス すべて lucene インデックス
メモ:
この条件は、データストアの設定が誤っていることを示しており、ANY バイナリ(アセットのバイナリなど)が見つからなくなる可能性があります。
この場合は、正常であることがわかっている最新のリポジトリバージョンに復元し、見つからないすべてのバイナリを回復します。
適用対象:
症状:
検証方法:
The AsyncIndexUpdate
(5 秒ごとに)失敗し、error.log に例外が記録されます。
...a Lucene index file is corrupt...
解決方法:
AEM 6.5 では、 oak-run.jar は ONLY でサポートされているメソッドです。 MongoMK または RDBMK リポジトリのインデックス再作成用。
用途 oak-run.jar プロパティインデックスを再作成するには
プロパティインデックスで async-reindex プロパティを true に設定します。
[oak:queryIndexDefinition]@reindex-async=true
Web コンソールで、 PropertyIndexAsyncReindex MBean;
例:
用途 再インデックスする oak-run.jar Lucene プロパティインデックス。
Lucene プロパティインデックスで async-reindex プロパティを true に Lucene プロパティインデックス
[oak:queryIndexDefinition]@reindex-async=true
前の節では、Oak のインデックス再作成に関するガイダンスを、 Apache Oak ドキュメント AEMのコンテキストで使用されます。
テキスト事前抽出は、分離されたプロセスのデータストアから直接バイナリのテキストを抽出して処理し、抽出されたテキストを後続の Oak インデックスの再インデックス/インデックス作成に直接公開するプロセスです。
/oak:index/damAssetLucene
.のインデックス再作成 既存 バイナリ抽出が有効な lucene インデックス
バイナリ抽出が有効になった新しい Lucene インデックスの AEM へのデプロイメントのサポート
テキスト事前抽出は、リポジトリに追加された新しいコンテンツには使用できません。また、使用する必要もありません。
リポジトリに追加された新しいコンテンツには、非同期フルテキストインデックス作成プロセスによって、インデックスが必然的かつ増分的に作成されます(デフォルトでは 5 秒ごと)。
AEMの通常の操作(Web UI を使用したアセットのアップロードや、プログラムによるアセットの取り込みなど)では、AEMは、新しいバイナリコンテンツのフルテキストインデックスを自動的かつ増分的に作成します。 データの量は増分であり、比較的少ないので(およそ 5 秒でリポジトリに永続化できるデータの量)、AEM では、全体的なシステムパフォーマンスに影響を及ぼすことなく、インデックス作成時にバイナリからのフルテキスト抽出を実行できます。
フルテキストバイナリ抽出を実行する Lucene インデックスを再インデックスするか、既存のコンテンツのフルテキストインデックスバイナリを使用する新しいインデックスをデプロイします。
テキストを事前抽出する元のコンテンツ(バイナリ)がリポジトリ内に存在すること
CSV ファイルを生成し、最終的なインデックス再作成を実行するためのメンテナンスウィンドウ
Oak バージョン 1.0.18 以降、1.2.3 以降
oak-run.jar バージョン 1.7.4 以降
インデックス作成AEMインスタンスからアクセス可能な抽出されたテキストを保存するファイルシステムフォルダー/共有
以下で概説する oak-run.jar コマンドを列挙した完全なリストは、https://jackrabbit.apache.org/oak/docs/query/pre-extract-text.html を参照してください。
上の図と後述の手順は、Apache Oak のドキュメントに記載されているテキスト事前抽出の技術的な手順を解説および補足しています。
事前抽出する内容のリストの生成
この操作中にノードストアがトラバースされるので、メンテナンス期間や低使用期間中にステップ 1(a~b) を実行します。これにより、システムに大きな負荷がかかる場合があります。
1a. 実行 oak-run.jar --generate
をクリックして、事前に抽出されたテキストを持つノードのリストを作成します。
1b. ノードのリスト(1a)が CSV ファイルとしてファイルシステムに格納されます。
ノードストア全体が(oak-run コマンドのパスで指定されたとおりに)毎回トラバースされます。 --generate
が実行され、 新規 CSV ファイルが作成されました。 CSV ファイルは not テキストの事前抽出プロセスを個別に実行する間に再利用されます(手順 1 ~ 2)。
ファイルシステムへのテキストの事前抽出
手順 2(a ~ c)は、AEM の通常の操作中に実行できます。この手順では、データストアのみとやり取りがおこなわれます。
2a. 実行 oak-run.jar --tika
(1b) で生成された CSV ファイルに列挙されたバイナリノードのテキストを事前に抽出する
2b. (2a)で開始されたプロセスが、CSV で定義されているバイナリノードにデータストアで直接アクセスし、テキストを抽出します。
2c.抽出されたテキストは、Oak のインデックス再作成プロセス (3a) で取り込み可能な形式でファイルシステムに保存されます。
事前抽出されたテキストは、CSV 内でバイナリのフィンガープリントによって識別されます。バイナリファイルが同じである場合、事前抽出された同じテキストを AEM インスタンス間で使用できます。AEM Publish は通常AEM Author のサブセットなので、AEM Author の事前に抽出されたテキストは、AEM Publish のインデックス再作成にもよく使用できます (AEM Publish が抽出されたテキストファイルへのファイルシステムアクセス権を持っている場合 )。
事前抽出されたテキストは、時間が経つにつれて増分的に追加されることがあります。テキストの事前抽出では、以前に抽出したバイナリの抽出がスキップされるので、後でインデックス再作成が必要になる場合(抽出したコンテンツが大きくないと仮定)に備えて事前抽出済みのテキストを保持することをお勧めします。 過度に大きい場合は、テキストが十分に圧縮される zip 形式で内容を暫定的に圧縮することを検討してください)。
Oak インデックスの再インデックス、抽出されたテキストファイルからフルテキストを取得
この操作中にノードストアがトラバースされるので、メンテナンス中や低使用時にインデックス再作成(手順 3a~b)を実行します。これにより、システムに大きな負荷がかかる場合があります。
3a. 再インデックス AEMで Lucene インデックスが呼び出されます。
3b. Apache Jackrabbit Oak DataStore PreExtractedTextProvider の OSGi 設定(抽出されたテキストをファイルシステムパスで指定します)では、Oak は、抽出されたファイルからフルテキストを取得するよう指示されており、リポジトリに格納されているデータに Oak が直接アクセスして処理することを回避します。