Lucene-MergePolicy详解

news/2024/11/29 18:27:34/

简介


该文章基于业务需求背景,因场景需求进行参数调优,下文会尽可能针对段合并策略(SegmentMergePolicy)的全参数进行说明。

主要介绍TieredMergePolicy,它是Lucene4以后的默认段的合并策略,之前采用的合并策略为LogMergePolicy,建议自行熟悉LogMergePolicy后再了解TieredMergePolicy,这样对于两种段合并策略的优缺点就一目了然,后续即可根据不同业务使用对应的策略,下面对两种策略的差异做一个简单总结:

  1. LogMergePolicy总是合并相邻的段文件,合并相邻的段文件(Adjacent Segment)描述的是对于IndexWriter提供的段集,LogMergePolicy会选取连续的部分(或全部)段集区间来生成一个待合并段集;
  2. TieredMergePolicy中会先对IndexWriter提供的段集进行排序,然后在排序后的段集中选取部分(可能不连续)段来生成一个待合并段集,即非相邻的段文件(Non-adjacent Segment)。

一句话描述:TieredMergePolicy:找出大小接近且最优的段集。下面对该概括进行详细分析。频繁且大量的段合并会造成进程CPU飙升。

TieredMergePolicy


TieredMergePolicy的一些参数


合并类型(MERGE_TYPE)


MERGE_TYPE中描述了IndexWriter在不同状态下调用合并策略的三种类型:

  • NATURAL:IndexWriter对缩影执行变更操作后调用合并策略;
  • FORCE_MERGE:IndexWriter需要需要将索引中包含所有的段集数量(total set of segments in the index)合并为指定数量;
  • FORCE_MERGE_DELETES:IndexWriter需要将索引中包含所有的段中的被删除文件进行抹去(expunge)操作。

三中类型触发过程处理逻辑一致,下文仅对NATURAL进行介绍。

maxMergeAtOnce(可配置)

maxMergeAtOnce的缺省值为10,描述了在NATURAL类型下执行一次合并操作最多包含的段的个数(Maximum number of segments to be merged at a time during "normal" mergin)。

segsPerTier(可配置)

segsPerTier的默认值为10,描述了每一层(层级的概念类似LogMergePolicy,这里不做赘述)中需要包含segsPerTier个段才允许合并,例外情况就是当段集中包含的被删除的文档数量达到某个值(下文会介绍),就不用考虑segsPerTier中的段的个数。

mergeFactor

mergeFactor描述了执行一次合并操作最多包含的段的个数,该值计算方式如下:

final int mergeFactor = (int) Math.min(maxMergeAtOnce, segsPerTier);

段大小(SegmentSize)


SegmentSize描述了一个段的大小,他是该段中除去被删除文档的索引信息的所有索引文件的大小的综合。

maxMergedSegmentBytes(可配置)

maxMergedSegmentBytes缺省值为5G,它有两个用途:

  1. 限制合并段集大小总量:待合并的段集大小总和不能超过该值;
  2. 限制大段(Segment with huge size)合并:该值的一半,即(maxMergedSegmentBytes / 2.0)用来描述某个段如果大小超过就不参与合并(限制大段还要同时满足被删除文档的条件,在下文会介绍)

另外值得一提的是,该值为内存杀手。内存有限的情况下,该值为必配项(5G的merge行为,在非受控堆外,将内存撑满)如下pmap图可见一般:

hitTooLarge

hitTooLarge是一个布尔值,当OneMerge中所有段的大小总和接近maxMergedSegmentBytes,hitTooLarge会被置为true,该值影响OneMerge的打分。

deletesPctAllowed(可配置)

deletesPctAllowed的默认值为33(百分比),自定义该值时允许的值域为[20,50],该值有两个用途:

  1. 限制大段合并:需要满足该段的SegmentSize≥(maxMergedSegmentBytes/2.0);并且满足段集中的被删除文档的索引信息大小占总索引文件大小的比例totalDelPct≤deletedPctAllowed或该段中被删除文档的索引信息大小占段中索引文件大小的比例segDelPct≤deletesPctAllowed,如下判断逻辑:
    (SegmentSize > (maxMergedSegmentBytes / 2)) && (totalDelPct <= deletesPctAllowed || segDelPct <= deletesPctAllowed)
  2. 计算allowedDelCount:计算公式如下,其中totalMaxDoc描述了段集中除去被删除文档的文档数量总和,allowedDelCount的介绍见下文:
    int allowedDelCount = (int) (deletesPctAllowed * totalMaxDoc / 100);
allowedSegCount、allowedDelCount
  • allowedSegCount:该值描述了段集内每个段的大小SegmentSize是否比较接近(segments of approximately equal size),根据当前索引大小来估算当前索引中"应该"有多少个段,如果实际的段个数小于估算值,那么说明索引中的段不满足差不多都相同(approximately equal size),那么就不会选出OneMerge(这里不赘述该名词含义,见LogMergePolicy)。allowedSegCount的最小值为segsPerTier,allowedSegCount的值越大,索引中会堆积更多的段,说明IndexWriter提交的段集(不包含大段)中最大的段的MaxSegmentSize跟最小的段MinSegmentSize相差越大,或者最小的段MinSegmentSize占段集总大小totalSegmentSize的占比特别低,一个原因在于有些flush()或者commit()的文档数相差太大,另一个原因是可配置参数floorSegmentBytes值设置的太小。
  • allowedDelCount:描述了IndexWriter提交的段集(不包含大段)中包含的被删除文档数量,在NATURAL类型下,当某个段集中的成员个数不满足allowedSegCount时,但是如果该段集(不包含大段)中包含的被删除的文档数量大于allowedDelCount,那么该段集还可以继续参与剩余的合并策略的处理(因为执行段的合并的一个非常重要的目的就是"干掉"被删除的文档号),否则就该段集此次不生成一个oneMerge。
floorSegmentBytes(可配置,重要!!!)

floorSegmentBytes缺省值为2MB(2 * 1024 * 1024),该值描述了段的大小segmentSize小于floorSegmentBytes的段,他们的segmentSize都当做floorSegmentBytes。

(源码原文:Segments smaller than this are "rounded up" to this size, ie treated as equal (floor) size for merge selection)
使计算出来的allowedSegCount较小,这样能尽快的将小段(tiny Segment)合并,另外该值还会影响OneMerge的打分(下文会介绍)。

设置了不合适的floorSegmentBytes后会发生以下的问题:

  • floorSegmentBytes的值太小:导致allowedSegCount很大(allowedSegCount=n*segsPerTier+m 0≤m≤segsPerTier , n≥1),特别是段集中最小的段MinSegmentSize占段集总大小totalSegmentSize的占比特别低,最终使得索引中一段时间存在大量的小段,因为段集的总数小于等于allowedSegCount是不会参与段合并的(如果不满足allowedDelCount的条件)。源码中解释floorSegmentBytes的用途的原文为: This is to prevent frequent flushing of tiny segments from allowing a long tail in the index;
  • floorSegmentBytes的值太大:导致allowedSegCount很小(最小值为segsPerTier),即较大的段合并可能更频繁,段越大,合并开销(合并时间,线程频繁占用)越大(在后面的文章中会介绍索引文件的合并)。

SegmentSize多小为小段(tiny Segment),这个定义取决于不同的业务,如果某个业务中认为小于TinySegmentSize的段都为小段,那么floorSegmentBytes的值大于TinySegmentSize即可。

段合并流程图


详细处理逻辑可以通过阅读Lucene源码得到(org.apache.lucene.index.TieredMergePolicy#findMerges),如下代码段为入口:

public MergeSpecification findMerges(MergeTrigger mergeTrigger, SegmentInfos infos, MergeContext mergeContext) throws IOException {final Set<SegmentCommitInfo> merging = mergeContext.getMergingSegments();// Compute total index bytes & print details about the indexlong totIndexBytes = 0;long minSegmentBytes = Long.MAX_VALUE;int totalDelDocs = 0;int totalMaxDoc = 0;long mergingBytes = 0;List<SegmentSizeAndDocs> sortedInfos = getSortedBySegmentSize(infos, mergeContext);Iterator<SegmentSizeAndDocs> iter = sortedInfos.iterator();while (iter.hasNext()) {SegmentSizeAndDocs segSizeDocs = iter.next();final long segBytes = segSizeDocs.sizeInBytes;if (verbose(mergeContext)) {String extra = merging.contains(segSizeDocs.segInfo) ? " [merging]" : "";if (segBytes >= maxMergedSegmentBytes) {extra += " [skip: too large]";} else if (segBytes < floorSegmentBytes) {extra += " [floored]";}message("  seg=" + segString(mergeContext, Collections.singleton(segSizeDocs.segInfo)) + " size=" + String.format(Locale.ROOT, "%.3f", segBytes / 1024 / 1024.) + " MB" + extra, mergeContext);}if (merging.contains(segSizeDocs.segInfo)) {mergingBytes += segSizeDocs.sizeInBytes;iter.remove();// if this segment is merging, then its deletes are being reclaimed already.// only count live docs in the total max doctotalMaxDoc += segSizeDocs.maxDoc - segSizeDocs.delCount;} else {totalDelDocs += segSizeDocs.delCount;totalMaxDoc += segSizeDocs.maxDoc;}minSegmentBytes = Math.min(segBytes, minSegmentBytes);totIndexBytes += segBytes;}assert totalMaxDoc >= 0;assert totalDelDocs >= 0;final double totalDelPct = 100 * (double) totalDelDocs / totalMaxDoc;int allowedDelCount = (int) (deletesPctAllowed * totalMaxDoc / 100);// If we have too-large segments, grace them out of the maximum segment count// If we're above certain thresholds of deleted docs, we can merge very large segments.int tooBigCount = 0;iter = sortedInfos.iterator();// remove large segments from consideration under two conditions.// 1> Overall percent deleted docs relatively small and this segment is larger than 50% maxSegSize// 2> overall percent deleted docs large and this segment is large and has few deleted docswhile (iter.hasNext()) {SegmentSizeAndDocs segSizeDocs = iter.next();double segDelPct = 100 * (double) segSizeDocs.delCount / (double) segSizeDocs.maxDoc;if (segSizeDocs.sizeInBytes > maxMergedSegmentBytes / 2 && (totalDelPct <= deletesPctAllowed || segDelPct <= deletesPctAllowed)) {iter.remove();tooBigCount++; // Just for reporting purposes.totIndexBytes -= segSizeDocs.sizeInBytes;allowedDelCount -= segSizeDocs.delCount;}}allowedDelCount = Math.max(0, allowedDelCount);final int mergeFactor = (int) Math.min(maxMergeAtOnce, segsPerTier);// Compute max allowed segments in the indexlong 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 may occasionally be less than segsPerTier// if segment sizes are below the floor sizeallowedSegCount = Math.max(allowedSegCount, segsPerTier);if (verbose(mergeContext) && tooBigCount > 0) {message("  allowedSegmentCount=" + allowedSegCount + " vs count=" + infos.size() +" (eligible count=" + sortedInfos.size() + ") tooBigCount= " + tooBigCount, mergeContext);}return doFindMerges(sortedInfos, maxMergedSegmentBytes, mergeFactor, (int) allowedSegCount, allowedDelCount, MERGE_TYPE.NATURAL,mergeContext, mergingBytes >= maxMergedSegmentBytes);
}


http://www.ppmy.cn/news/1129237.html

相关文章

模拟实现C语言中经典库函数,字符相关的函数与内存相关的函数

前言&#xff1a;C语言中拥有非常多的库函数&#xff0c;仅仅知道它们是不够的&#xff0c;在知道它们的原理后&#xff0c;去模拟实现它能够帮助我们更好的掌握这些库函数。 PS(在面试时&#xff0c;部分企业会让你来模拟实现一些库函数) 文章目录&#xff1a;模拟库函数的实现…

RISC-V 基础指令汇总

加载指令 存储指令 PC寻址指令 auipc rd, imm这条指令把 imm &#xff08;立即数&#xff09;左移12位并带符号扩展到64位后&#xff0c;得到一个新的立即数&#xff0c;这个新的立即数是一个有符号的立即数&#xff0c;再加上当前 PC 值&#xff0c;然后存储到 rd 寄存器中。…

Vovsoft Text Edit Plus 专业文本编辑器工具软件:简洁高效的创作利器

作为一名专业软件评测人员&#xff0c;我有幸使用了一款备受赞誉的文本编辑器工具软件——Vovsoft Text Edit Plus。在这篇评测中&#xff0c;我将客观、细致地分析它的实用性和使用场景&#xff0c;同时揭示它的优缺点&#xff0c;帮助您更好地了解这款软件。 第一部分&#x…

贪心算法-点灯问题

1、题目描述 给定一个字符串str&#xff0c;只由 ‘X’ 和 ‘.’ 两种字符构成。‘X’ 表示墙&#xff0c;不能放灯&#xff0c;点亮不点亮都可&#xff1b;’.’ 表示居民点&#xff0c;可以放灯&#xff0c;需要点亮。如果灯放在i位置&#xff0c;可以让 i-1&#xff0c;i 和…

目标检测YOLO实战应用案例100讲-面向辅助驾驶的道路目标检测

目录 前言 国内外研究现状 机器学习目标检测算法研究现状

0x84加密数据传输服务

为了在安全模式下实现一些诊断服务&#xff0c;在服务端和客户端应用程序之间添加了Security sub-layer。在客户端与服务端之间进行诊断服务数据传输有两种方法&#xff1a; 1、非安全模式下数据传输   应用程序使用诊断服务(diagnostic Services)和应用层服务原语(Applicati…

基于微信小程序的宠物寄养平台小程序设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言系统主要功能&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…

网络爬虫--伪装浏览器

从用户请求的Headers反反爬 在访问某些网站的时候&#xff0c;网站通常会用判断访问是否带有头文件来鉴别该访问是否为爬虫&#xff0c;用来作为反爬取的一种策略。很多网站都会对Headers的User-Agent进行检测&#xff0c;还有一部分网站会对Referer进行检测&#xff08;一些资…