Lucene4.7 Directory (一)

散仙今天就從源碼的角度來分析下Lucene的根基Directory的實現,在此以前,咱們先來看下Directory家族的層級分佈圖。
java

從上圖中,咱們能夠看出Directory共有11個直接或者間接的子類,不一樣的子類的做用和功能不同,那麼Directory做爲此繼承圖的頂級父類,在Lucene中確實發揮重要的根基做用,就像Hadoop的根基是HDFS同樣,Directory肩負着索引存儲的重任,若是沒有存儲,那麼檢索就無從談起了,雖然咱們常常稱全文檢索,搜索引擎什麼的,其實它們的背後,Directory纔是默默無聞的」雷鋒「。
web

下面就來詳細的剖析下Directory的核心實現。 
Directory是由lucene中的一些列索引文件組成的目錄,一個典型索引文件結構圖的截圖以下:
多線程

而Directory的做用,就是負責管理這些索引文件,包括數據的讀取和寫入,以及索引文件的添加,刪除和合並。從這樣的角度來分析,Directory更像一個系統的管理員,下面,散仙再具體的分析下一些核心方法的做用。 

咱們都知道Lucene的索引體系,支持讀共享,寫獨佔的方式來訪問索引目錄,也就是說,它容許多個線程實例同時併發的讀取,而不容許多個線程同時寫入,你們可能會有疑問,爲何不支持多線程寫入呢?這實際上是由於索引目錄有本身的某一時刻的內部狀態,好比說文件指針,而多線程寫入時,會形成指針混亂,從而引發索引結構損壞或某些數據丟失,因此lucene任什麼時候候都禁止有多個線程併發的寫入索引,即便是多線程寫,每次也只能經過隊列的方式,一次只容許一個線程操做索引,按這樣的狀況分析,多線程寫入與單線程寫入,在性能上的提高,並非明顯的,那麼lucene又是怎麼控制一次只能有一個線程寫入呢,打開Directory的源碼,咱們就會發現,它實際上是在內部維護了一個鎖的實例,經過加鎖方式,來禁止後來線程的寫入操做,固然鎖的做用不只僅是防止併發寫入,它還能夠經過鎖名字來判斷,這兩份索引是否爲同一份索引,那麼若是咱們想使用多線程來提高寫入速度,一個折中的辦法就是,每一個線程寫一份目錄,最後在對這些目錄,進行合併,下面給出了一些源碼中鎖的實現方法
併發

protected LockFactory lockFactory;//鎖實現,只能由子類覆蓋
//設置鎖名
  public Lock makeLock(String name) {
      return lockFactory.makeLock(name);
  }
  //清除鎖
  public void clearLock(String name) throws IOException {
    if (lockFactory != null) {
      lockFactory.clearLock(name);
    }
  }

下面咱們來分析下Directory源碼中另一個變量isOpen的做用
oop

    //注意,使用的是volatile關鍵字修飾
  volatile protected boolean isOpen = true;

isOpen是用來判斷當前的Directory實例,在內存中的狀態,它使用的是volatile 關鍵字修飾的,被此變量修飾的內容,JVM虛擬機讀取的時候會直接在主存中讀取該變量的值,而不會在各個線程的本地內存中讀,這樣一來,當併發讀的時候,若是Directory實例關閉了,那麼各個讀的線程會當即獲取最新的狀態,若是不作處理的話,將會拋出一個目錄實例關閉的異常。isOpen 確保了索引在併發讀的時候,各個線程實例獲取Directory狀態的一致性。
性能

  private static final class SlicedIndexInput extends BufferedIndexInput {
    IndexInput base;
    long fileOffset;
    long length;
    
    SlicedIndexInput(final String sliceDescription, final IndexInput base, final long fileOffset, final long length) {
      this(sliceDescription, base, fileOffset, length, BufferedIndexInput.BUFFER_SIZE);
    }
    
    SlicedIndexInput(final String sliceDescription, final IndexInput base, final long fileOffset, final long length, int readBufferSize) {
      super("SlicedIndexInput(" + sliceDescription + " in " + base + " slice=" + fileOffset + ":" + (fileOffset+length) + ")", readBufferSize);
      this.base = base.clone();
      this.fileOffset = fileOffset;
      this.length = length;
    }

接下來,來分析Directory的靜態常量內部類SlicedIndexInput的做用,Lucene的索引文件是很是鬆散的,不一樣類型的數據存儲在不一樣的文件裏,咱們能夠經過文件名,來單獨讀取指定索引文件的內容,一樣道理咱們也能夠,在寫入信息時候,單獨寫入某部分數據的信息,這樣一來,就避免了操做整個目錄的可能,按需所用,從必定程度上來講,這樣的設計提高了性能,保證了數據的穩定與可靠性,雖然也從某種程度上加大了Directory目錄管理的複雜度,但這些都是微不足道的。 


SlicedIndexInput這個類的做用保證了Lucene能夠單獨讀取部分索引文件的內容,注意這些內容都不是最原始的數據,而是SlicedIndexInput克隆的一份副本,這樣一來在併發讀的環境下是很是有利的,每一個線程都會從主存中load一份副本出來。在咱們的源碼中,咱們並無發現它具備深度克隆的功能,可是經過一系列繼承的追蹤,咱們發現,SlicedIndexInput==》BufferedIndexInput==》IndexInput==》DataInput,在最後的這個父類中實現了Cloneable和Closeable接口,從而確保保證了SlicedIndexInput能夠正常的工做,以及釋放一些佔用的IO資源。
 


除了上面幾個比較重要的做用外,Directory還提供了,其餘的一些文件管理功能,例如獲取全部的索引文件信息,刪除一個索引文件,獲取一個索引文件的大小,索引的備份,等等在這裏散仙,就不給出演示了,此篇文章重點分析的Directory的功能和做用,後續的文章,散仙會重點分析它的一些子類的實現和功能。
this

相關文章
相關標籤/搜索