検証した環境
- Solr:8.6(Cloud シャード:1,NRT:2)
セグメント(Segment)とは
Solrはコミットをするとインデックスに書き込みをします、内部的にはセグメントと呼ばれる単位で分割されています。
Segmentはインデックスを更新してコミットを実行するたびに新しいセグメントが作成されます。
例1
100件のドキュメントを登録してコミットする
パラメータ | 数値 |
---|---|
Num Docs | 100 |
MaxDoc | 100 |
Deleted Docs | 0 |
Segment Count | 1 |
_0 -> docs:100, dels:0
更に30件新規のドキュメントを登録してコミットする
パラメータ | 数値 |
---|---|
Num Docs | 130 |
MaxDoc | 130 |
Deleted Docs | 0 |
Segment Count | 2 |
_0 -> docs:100, dels:0
_1 -> docs:30, dels:0
あとから登録した30件は_1のセグメントに含まれます。
例2
次は100件登録してコミットしたあとに30件のドキュメントを更新してコミットしてみます。
パラメータ | 数値 |
---|---|
Num Docs | 100 |
MaxDoc | 130 |
Deleted Docs | 30 |
Segment Count | 2 |
_0 -> docs:100, dels:30
_1 -> docs:30, dels:0
ドキュメントの更新の場合は0のセグメントのうち、1のセグメントに含まれるドキュメントは削除済みとして扱われます。
セグメントのマージ
ドキュメントの追加、更新、削除を繰り返すとセグメント数が増えていきます。
セグメント数が多くなると検索性能が劣化していきます。
また例2の場合のように削除済みとしてマークされているものは検索時には使用されないですがディスク上に残ってしまうので無駄になってしまいます。
なのでsolrではセグメントをマージする機能があります。
セグメントのマージ方法
- optimizeによる強制マージ
- コミット時にMergePolicyによるマージ
- expungeDeletesによるマージ
optimizeによる強制マージ
※ optimizeによる強制マージはあまり推奨されていないようです。 MergePolicyを利用したマージを使うほうが主流のようです。
curl -X POST -H 'Content-Type: application/json' 'http://localhost:8981/solr/{collection}/update' --data-binary '
{
"optimize": {maxSegments:1}
}'
MergePolicyによるマージ
今回はデフォルトのマージポリシーのTieredMergePolicyについてまとめます。
マージに関連するsolrconfig.xmlの設定
<config>
<indexConfig>
<mergePolicyFactory class="org.apache.solr.index.TieredMergePolicyFactory">
<int name="segmentsPerTier">10</int>
<int name="floorSegmentMB">2</int>
<int name="deletesPctAllowed">33</int>
<int name="maxMergedSegmentMB">5000</int>
<int name="maxMergeAtOnce">10</int>
<int name="forceMergeDeletesPctAllowed">10</int>
</mergePolicyFactory>
</indexConfig>
</config>
マージのしきい値となる設定
segmentsPerTier(デフォルト値:10)
階層のSegment Countがしきい値(allowedSegCount)の設定値を超えると、次回のコミット時にマージが実行されます
SegmentCount > allowedSegCount
デフォルトの10だとSegment Countが11になるとしきい値を超えるので次のコミット時にマージされます。
(※ドキュメントのトータルのサイズが小さい時)
「0」から「9」がマージされbになる
allowedSegCountの算出方法
TieredMergePolicyでは以下のロジックでallowedSegCountが決定します。
final int mergeFactor = (int) Math.min(maxMergeAtOnce, segsPerTier);
long levelSize = Math.max(minSegmentBytes, floorSegmentBytes);
long bytesLeft = totIndexBytes;
double allowedSegCount = 0;
while (true) {
final double segCountLevel = bytesLeft / (double) levelSize;
if (segCountLevel < segsPerTier || levelSize == maxMergedSegmentBytes) {
allowedSegCount += Math.ceil(segCountLevel);
break;
}
allowedSegCount += segsPerTier;
bytesLeft -= segsPerTier * levelSize;
levelSize = Math.min(maxMergedSegmentBytes, levelSize * mergeFactor);
}
allowedSegCount = Math.max(allowedSegCount, segsPerTier);
floorSegmentBytes = floorSegmentMB * 1024 * 1024
solrconfig.xmlで指定できる、floorSegmentMBの値を上げるとallowedSegCountの値も上がりにくくなります。
floorSegmentMB=1と設定した場合
ドキュメントのトータルサイズが、10MBを超えるとallowedSegCount=12に変化します。
deletesPctAllowed(デフォルト値:33)
Deletionsの割合がdeletesPctAllowedを超えると、次回のコミット時にマージが実行されます
Deletions = \frac{Deleted Docs}{MaxDoc} * 100 > deletesPctAllowed
33%超えるとピンク色になるので次回のコミット時にマージされるのがわかります。
マージするセグメント数の設定
maxMergeAtOnce(デフォルト値:10)
マージが実行されるときにmaxMergeAtOnceの値までのセグメントがマージの対象となります
maxMergeAtOnce=2,maxMergedSegmentMB=5000の場合
マージ対象がmaxMergeAtOnceの0,1だけが対象となります。
maxMergedSegmentMB(デフォルト値:5000)
マージが実行されるときにマージするセグメントの合計サイズがmaxMergedSegmentMBを超えない範囲が対象となります。
maxMergeAtOnce=10,maxMergedSegmentMB=1の場合
この場合は0から4までがmaxMergedSegmentMBの値に収まるのでマージの対象となっていることがわかります。
expungeDeletes
コミット時にexpungeDeletesというのを指定することができます。
curl -X POST -H 'Content-Type: application/json' 'http://localhost:8981/solr/{collection}/update' --data-binary '
{
"commit": {expungeDeletes: true}
}'
expungeDeletesを指定すると削除済みが含まれるセグメントがマージされます。
このときセグメント単体でみた削除のドキュメントの割合がforceMergeDeletesPctAllowed以下の場合は対象外となります。
expungeDeletesを実行
削除を含むセグメント0と1がマージされ4になりました。