日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

大量更新后数据膨胀_段合并的原理探寻

發(fā)布時間:2024/2/28 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 大量更新后数据膨胀_段合并的原理探寻 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

    • 問題
    • 問題探尋
    • lucene的段合并機制
      • 1. allowedSegCountInt 的計算方式
      • 2. 第5步的計算過程,就是排除正在合并的段
      • 3. 第7步的排列組合
      • 4. score函數(shù)的打分模型,
      • 總結(jié)

問題

??在上周的時候,有一個索引post,數(shù)據(jù)方突然做了一個近全表的update,導(dǎo)致了大量的數(shù)據(jù)更新。數(shù)據(jù)積累的情況下跑的有將近1個小時,但是奇怪的是在數(shù)據(jù)更新完以后索引膨脹了80%。從11g變成了20g.
當時懷疑是段合并的問題,但是看具體的segment數(shù)量卻基本上沒有變化。很是奇怪。經(jīng)歷了半天的時間,依然沒有降下來。后來打算在這周一如果數(shù)據(jù)量依然是很高的話就直接重建全量索引。
??在周一的時候,再看數(shù)據(jù)的量已經(jīng)降下來了,到了12g

GET _cat/indices/post?vhealth status index uuid pri rep docs.count docs.deleted store.size pri.store.size green open post_two vVGUNqf9RfyqPwIei7VD4A 5 2 10011876 2774293 12.2gb 3.9gb

問題探尋

??當時覺得很是奇怪,而且可以確定是段合并的問題了。也借著這個機會學習一下段合并的原理。
網(wǎng)上查找了不少資料,比較少,但是仔細找還是能夠找到的,當然,最后真正弄明白還是要靠源碼。

??這里只講一下es的index過程,es的index是過程是新段和刪除端不斷產(chǎn)生的過程。lucene不會直接刪除已經(jīng)存在的segment中的數(shù)據(jù),而是產(chǎn)生新的刪除段關(guān)聯(lián)原數(shù)據(jù)段。同時如果是update的話也會創(chuàng)建新的段。

??在這里,我們的情況就是產(chǎn)生了大量的update操作,雖然有些老段中已經(jīng)有很多刪除了,但是因為lucene的段合并策略,總是不能被合并,所以就導(dǎo)致了問題。

lucene的段合并機制

  • 先將所有段按照扣除刪除文檔之后的字節(jié)數(shù)(bytesize * (1.0 - delRatio))降序排列
  • 排除size>2.5G的段(2.5=maxMergedSegmentSize / 2)maxMergedSegmentBytes是一個設(shè)置值,默認為5g,最大合并段
  • 根據(jù)2的結(jié)果計算allowedSegCountInt,也就是當前索引對應(yīng)的數(shù)據(jù)量理想化的情況下應(yīng)該有多少個seg
  • 排除正在合并的段
  • 在以上幾個步驟后得到候選段集合eligible[]
  • 判斷實際的候選段的數(shù)量(eligible.size)是否大于 allowedSegCountInt ,如果小于,直接返回,不再進行合并,反之進入第7步
  • 對eligible進行降序的排列組合,得到多個組合結(jié)果(每個組合結(jié)果都是eligible的一個子集)
  • 對多個組合進行打分,得分最低的一組segment被選中進行合并。
  • 1. allowedSegCountInt 的計算方式

    第三步allowedSegCountInt 的計算方式

  • 選定最開始計算的階梯值,邏輯如下面的floorSize()函數(shù)
    • 計算,按照每一層的segsPerTier個段的數(shù)據(jù)量進行計算,層級是從小到大,直到無法達到某個層級的segsPerTier的個數(shù)。allowedSegCountInt,是理想化的情況下這些數(shù)據(jù)應(yīng)該有的segment數(shù)量,是為了衡量當前的段數(shù)量是否合理,如果是合理的狀態(tài),那么就可能不會在進行段合并了。
  • minSegmentBytes = floorSize(minSegmentBytes);// Compute max allowed segs in the indexlong levelSize = minSegmentBytes;//totIndexBytes是出掉大段后的所有段的總量long bytesLeft = totIndexBytes;double allowedSegCount = 0;while(true) {final double segCountLevel = bytesLeft / (double) levelSize;// segsPerTier是設(shè)定值,是每個階梯的段數(shù)量,默認值是10// 當段的剩余的bytesLeft在某個級別的段上已經(jīng)不夠segsPerTier的時候就會中斷循環(huán)// 說明已經(jīng)統(tǒng)計完了,沒有多余的bytesLeft來統(tǒng)計了,這也是唯一的中斷出口if (segCountLevel < segsPerTier) {allowedSegCount += Math.ceil(segCountLevel);break;}//每個層級的段數(shù)量相加allowedSegCount += segsPerTier;bytesLeft -= segsPerTier * levelSize;levelSize *= maxMergeAtOnce;}int allowedSegCountInt = (int) allowedSegCount; floorSegmentBytes是設(shè)定的最小段,理論上低于這個值的段都被認為是小段,默認的設(shè)置是2mprivate long floorSize(long bytes) {return Math.max(floorSegmentBytes, bytes);}

    2. 第5步的計算過程,就是排除正在合并的段

    final List<SegmentCommitInfo> eligible = new ArrayList<>();for(int idx = tooBigCount; idx<infosSorted.size(); idx++) {final SegmentCommitInfo info = infosSorted.get(idx);if (merging.contains(info)) {mergingBytes += size(info, writer);} else if (!toBeMerged.contains(info)) {eligible.add(info);}}

    3. 第7步的排列組合

    就是使用雙層for循環(huán),從大到小進行組合,segment的總數(shù)不超過10個,segment總的數(shù)據(jù)量不超過maxMergedSegmentBytes。
    如果在遍歷中有超過maxMergedSegmentBytes的時候?qū)itTooLarge置標志位,會影響打分
    打分越低越優(yōu)先。最后得到一個打分最低的segment組合。

    if (eligible.size() > allowedSegCountInt) {// OK we are over budget -- find best merge!MergeScore bestScore = null;List<SegmentCommitInfo> best = null;boolean bestTooLarge = false;long bestMergeBytes = 0;// Consider all merge starts:// 使用雙層for循環(huán),從大到小列舉所有可能for(int startIdx = 0;startIdx <= eligible.size()-maxMergeAtOnce; startIdx++) {long totAfterMergeBytes = 0;//candidate 保存一次組合的結(jié)果final List<SegmentCommitInfo> candidate = new ArrayList<>();boolean hitTooLarge = false;for(int idx = startIdx;idx<eligible.size() && candidate.size() < maxMergeAtOnce;idx++) {final SegmentCommitInfo info = eligible.get(idx);final long segBytes = size(info, writer);//如果加入這個段(info)以后總量大于 設(shè)定的最大合并段的值,就再排除這個段,但是置標志位hitTooLarge,表明當前組合的總量是接近最大段的,相對來說合并的效率更好,后期的話就不用再合并了if (totAfterMergeBytes + segBytes > maxMergedSegmentBytes) {hitTooLarge = true;// NOTE: we continue, so that we can try// "packing" smaller segments into this merge// to see if we can get closer to the max// size; this in general is not perfect since// this is really "bin packing" and we'd have// to try different permutations.//這里也說了,這個策略并不算完美continue;}candidate.add(info);totAfterMergeBytes += segBytes;}// We should never see an empty candidate: we iterated over maxMergeAtOnce// segments, and already pre-excluded the too-large segments:assert candidate.size() > 0;//這個地方進行打分final MergeScore score = score(candidate, hitTooLarge, mergingBytes, writer);if (verbose(writer)) {message(" maybe=" + writer.segString(candidate) + " score=" + score.getScore() + " " + score.getExplanation() + " tooLarge=" + hitTooLarge + " size=" + String.format(Locale.ROOT, "%.3f MB", totAfterMergeBytes/1024./1024.), writer);}// If we are already running a max sized merge// (maxMergeIsRunning), don't allow another max// sized merge to kick off://score小的優(yōu)先級更高if ((bestScore == null || score.getScore() < bestScore.getScore()) && (!hitTooLarge || !maxMergeIsRunning)) {best = candidate;bestScore = score;bestTooLarge = hitTooLarge;bestMergeBytes = totAfterMergeBytes;}}if (best != null) {if (spec == null) {spec = new MergeSpecification();}final OneMerge merge = new OneMerge(best);spec.add(merge);for(SegmentCommitInfo info : merge.segments) {toBeMerged.add(info);}if (verbose(writer)) {message(" add merge=" + writer.segString(merge.segments) + " size=" + String.format(Locale.ROOT, "%.3f MB", bestMergeBytes/1024./1024.) + " score=" + String.format(Locale.ROOT, "%.3f", bestScore.getScore()) + " " + bestScore.getExplanation() + (bestTooLarge ? " [max merge]" : ""), writer);}} else {return spec;} } else {return spec;}

    4. score函數(shù)的打分模型,

    這里記住,一定是分數(shù)越小優(yōu)先級越高
    1. 給出初始值 skew (如果在3中的hitTooLarge為true的話 該值為0.1,否則是 當前組合中最大段size/ 組合中所有段floorsize之和),floorsize是每個段所在的層的閾值,也就是大段和小段小段搭配更優(yōu)
    2. score=skew*Math.pow(totAfterMergeBytes, 0.05)//這個地方索命壓縮后size越小越好,這一點說明是小段優(yōu)先
    3. score = score*Math.pow(nonDelRatio, 2)//留存比率的平方,這個2是可以手動調(diào)節(jié)的,刪除率越高越好

    /** Expert: scores one merge; subclasses can override. */protected MergeScore score(List<SegmentCommitInfo> candidate, boolean hitTooLarge, long mergingBytes, IndexWriter writer) throws IOException {long totBeforeMergeBytes = 0;long totAfterMergeBytes = 0;long totAfterMergeBytesFloored = 0;for(SegmentCommitInfo info : candidate) {final long segBytes = size(info, writer);totAfterMergeBytes += segBytes;totAfterMergeBytesFloored += floorSize(segBytes);totBeforeMergeBytes += info.sizeInBytes();}// Roughly measure "skew" of the merge, i.e. how// "balanced" the merge is (whether the segments are// about the same size), which can range from// 1.0/numSegsBeingMerged (good) to 1.0 (poor). Heavily// lopsided merges (skew near 1.0) is no good; it means// O(N^2) merge cost over time:final double skew;//這里如果大段置位了,也就是這一組曾經(jīng)超過過大段,雖然后來又替換,但是應(yīng)該是接近大段的。這樣下次就不用合并了,所以給的優(yōu)先級比較高// 這樣的話就會給更高的優(yōu)先級if (hitTooLarge) {// Pretend the merge has perfect skew; skew doesn't// matter in this case because this merge will not// "cascade" and so it cannot lead to N^2 merge cost// over time:skew = 1.0/maxMergeAtOnce;} else {//對于其他情況,就用這個組合中最大的segment的size 除以組合內(nèi)所有元素的size,理論上除非組合中所有元素一樣大,否則,skew肯定大于0.1, 段的差異越大,這個值越大skew = ((double) floorSize(size(candidate.get(0), writer)))/totAfterMergeBytesFloored;}// Strongly favor merges with less skew (smaller// mergeScore is better):// mergeScore 越小越好double mergeScore = skew;// Gently favor smaller merges over bigger ones. We// don't want to make this exponent too large else we// can end up doing poor merges of small segments in// order to avoid the large merges://對merge后的總量size取指數(shù)運算,這樣說來,合并后總量越大對應(yīng)計算的mergeScore越大,優(yōu)先級也就越低, 越小則優(yōu)先級越高,但是,因為指數(shù)很小,所以影響不是很大,也就是更偏向于先合并小段// 輕輕地偏愛較小的合并而不是較大的合并,我們不想使此指數(shù)太大,否則我們最終可能會因為為了避免大合并而對小段進行不良合并mergeScore *= Math.pow(totAfterMergeBytes, 0.05);// Strongly favor merges that reclaim deletes://這個是保留率,就是刪除以后的總量和執(zhí)行刪除前的總量final double nonDelRatio = ((double) totAfterMergeBytes)/totBeforeMergeBytes;//這里看對壓縮比還是比較重視的,保有率越低越好,reclaimDeletesWeight是一個設(shè)置值,用來控制壓縮率在打分中所占的權(quán)重,默認是2,建議的是不超過3,如果是0的話,壓縮率就不影響打分了mergeScore *= Math.pow(nonDelRatio, reclaimDeletesWeight);final double finalMergeScore = mergeScore;return new MergeScore() {@Overridepublic double getScore() {return finalMergeScore;}@Overridepublic String getExplanation() {return "skew=" + String.format(Locale.ROOT, "%.3f", skew) + " nonDelRatio=" + String.format(Locale.ROOT, "%.3f", nonDelRatio);}};}

    總結(jié)

    總結(jié)來說就是

  • 如果10個seg合并后總量接近5G,那么就優(yōu)先級處于更高
  • 否則10個seg越均衡優(yōu)先級系數(shù)會加一些
  • 10個seg的總量更小,優(yōu)先級系數(shù)會大一些
  • 刪除率更高,優(yōu)先級系數(shù)更高
    綜合上面的幾個因素來考慮
    在update進行中的時候最開始的時候傾向于合并小段,小段優(yōu)先級更好,而且大量的index操作會產(chǎn)生大量的小段,之前的比較穩(wěn)定的中段沒有機會合并,所以刪除后的文檔也無法及時清理,等后面小段處理的差不多了,中段才有機會處理,并且存儲量也逐漸下來了。
  • 參考
    https://blog.csdn.net/duanduanpeng/article/details/72633217
    https://blog.csdn.net/jollyjumper/article/details/24786147
    https://blog.csdn.net/zhengxgs/article/details/78971141
    https://blog.csdn.net/kimichen123/article/details/77477251
    https://www.jianshu.com/p/9b872a41d5bb
    文檔刪除的原理,lucene
    https://blog.csdn.net/liujava621/article/details/40948417

    超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術(shù)人生

    總結(jié)

    以上是生活随笔為你收集整理的大量更新后数据膨胀_段合并的原理探寻的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。