1. 程式人生 > >淺談es的原理、機制 ,IK分詞原理

淺談es的原理、機制 ,IK分詞原理

1、分散式的架構es都有哪些機制?

1、主備
primary shard 的副本 replica shard
primary shard不能和自己的replica shard放在同一個節點上、

2、容錯
使用選舉機制
master node宕機,選舉master node,提升replica 為primary、
宕機的node重啟資料恢復

2、IK分詞原理

IK 分詞器,
1、詞典樹Tire Tree的構建,即將現在的詞典載入到一個記憶體結構中去
2、詞的匹配查詢,就是切詞
3、歧義判斷,即對不同切分方式的判定,哪種應是更合理的

2.1、詞典樹的構建

class DictSegment implements Comparable<DictSegment>{  
  
    //公用字典表,儲存漢字  
    private static final Map<Character , Character> charMap = new HashMap<Character , Character>(16 , 0.95f);  
    //陣列大小上限  
    private static final int ARRAY_LENGTH_LIMIT = 3;  
  
      
    //Map儲存結構  
private Map<Character , DictSegment> childrenMap; //陣列方式儲存結構 private DictSegment[] childrenArray; //當前節點上儲存的字元 private Character nodeChar; //當前節點儲存的Segment數目 //storeSize <=ARRAY_LENGTH_LIMIT ,使用陣列儲存, storeSize >ARRAY_LENGTH_LIMIT ,則使用Map儲存 private
int storeSize = 0; //當前DictSegment狀態 ,預設 0 , 1表示從根節點到當前節點的路徑表示一個詞 private int nodeState = 0; ……

ARRAY_LENGTH_LIMIT 的閥值來判斷,資料較小存在陣列,資料較大,存在HashMap 中;
如資料較小,存陣列,採用二分查詢的方式、
若資料較大,存在HashMap中時候,使用遞迴呼叫寫入字典樹,直接查詢的方式;

private synchronized void fillSegment(char[] charArray , int begin , int length , int enabled){  
 
     ……       
    //搜尋當前節點的儲存,查詢對應keyChar的keyChar,如果沒有則建立  
    DictSegment ds = lookforSegment(keyChar , enabled);  
    if(ds != null){  
        //處理keyChar對應的segment  
        if(length > 1){  
            //詞元還沒有完全加入詞典樹  
            ds.fillSegment(charArray, begin + 1, length - 1 , enabled);  
        }else if (length == 1){  
            //已經是詞元的最後一個char,設定當前節點狀態為enabled,  
            //enabled=1表明一個完整的詞,enabled=0表示從詞典中遮蔽當前詞  
            ds.nodeState = enabled;  
        }  
    }   
}  

2.2、切詞

切詞的2種方式
1、非smart模式
IK分詞輸出所有分詞、
2、smart模式
IK分詞器則會根據內在方法輸出一個認為最合理的分詞結果,這就涉及到了歧義判斷、

smart模式 張三 | 說的 | 確實 | 在理
非smart模式 張三 | 三 | 說的 | 的確 | 的 | 確實 | 實在 | 在理

IK中預設用到三個子分詞器,
LetterSegmenter(字母分詞器),CN_QuantifierSegment(量詞分詞器),CJKSegmenter(中日韓分詞器)。分詞是會先後經過這三個分詞器,我們這裡重點根據CJKSegment分析。其核心是一個analyzer方法。

public void analyze(AnalyzeContext context) {  
    …….  
          
        //優先處理tmpHits中的hit  
        if(!this.tmpHits.isEmpty()){  
            //處理詞段佇列  
            Hit[] tmpArray = this.tmpHits.toArray(new Hit[this.tmpHits.size()]);  
            for(Hit hit : tmpArray){  
                hit = Dictionary.getSingleton().matchWithHit(context.getSegmentBuff(), context.getCursor() , hit);  
                if(hit.isMatch()){  
                    //輸出當前的詞  
                    Lexeme newLexeme = new Lexeme(context.getBufferOffset() , hit.getBegin() , context.getCursor() - hit.getBegin() + 1 , Lexeme.TYPE_CNWORD);  
                    context.addLexeme(newLexeme);  
                      
                    if(!hit.isPrefix()){//不是詞字首,hit不需要繼續匹配,移除  
                        this.tmpHits.remove(hit);  
                    }  
                      
                }else if(hit.isUnmatch()){  
                    //hit不是詞,移除  
                    this.tmpHits.remove(hit);  
                }                     
            }  
        }             
          
        //*********************************  
        //再對當前指標位置的字元進行單字匹配  
        Hit singleCharHit = Dictionary.getSingleton().matchInMainDict(context.getSegmentBuff(), context.getCursor(), 1);  
        if(singleCharHit.isMatch()){//首字成詞  
            //輸出當前的詞  
            Lexeme newLexeme = new Lexeme(context.getBufferOffset() , context.getCursor() , 1 , Lexeme.TYPE_CNWORD);  
            context.addLexeme(newLexeme);  
 
            //同時也是詞字首  
            if(singleCharHit.isPrefix()){  
                //字首匹配則放入hit列表  
                this.tmpHits.add(singleCharHit);  
            }  
        }else if(singleCharHit.isPrefix()){//首字為詞字首  
            //字首匹配則放入hit列表  
            this.tmpHits.add(singleCharHit);  
        }  
   ……  
}  

2.3、歧義判斷

IKArbitrator(歧義分析裁決器)是處理歧義的主要類。