Lucene4.3進階開發之亂世叢生(二)

    在本篇將重點介紹下Directory的一個很重要的子類FSDirectory,爲何說此類很是重要呢?若是你是正在使用lucene的開發者,那麼就知道,咱們常用的一行代碼:
java

Directory directory=FSDirectory.open(new File(indexPath))

    經過這行代碼,咱們能夠獲取一個Directory的子類文件存儲目錄,而後咱們對索引的一些操做,都是以這個子類的文件目錄爲基礎的,下面從源碼的角度剖析下FSDirectory這個類的做用,在此以前用一個表格來介紹下lucene存儲索引的幾種方式:
安全

上面的幾種存儲方式是lucene目前爲止,可以支持良好的格式,那麼今天,就要介紹FSDirectory這類方式,就是上面圖標中,第三類基於文件系統存儲方式的根基,FSDirectory並非一個具體的文件目錄,一般狀況下,咱們使用的是FSDirectory下一個具體的子類(MMapDirectory、SimpleFSDirectory、NIOFSDirectory)來做爲咱們的索引目錄,那麼咱們可能有個很大的疑惑,咱們在實際開發者大部分時候並無直接指定具體使用的是哪一個目錄,爲何咱們還能正常使用它呢?
併發

先來看下常用的那個FSDirectory的open方法源碼是怎麼實現的:dom

/** Just like {@link #open(File)}, but allows you to
   *  also specify a custom {@link LockFactory}. */
  public static FSDirectory open(File path, LockFactory lockFactory) throws IOException {
    if ((Constants.WINDOWS || Constants.SUN_OS || Constants.LINUX)
          && Constants.JRE_IS_64BIT && MMapDirectory.UNMAP_SUPPORTED) {
      return new MMapDirectory(path, lockFactory);
    } else if (Constants.WINDOWS) {
      return new SimpleFSDirectory(path, lockFactory);
    } else {
      return new NIOFSDirectory(path, lockFactory);
    }
  }

事實上,咱們經過open方法,lucene底層一般跟咱們的jre的爲啥是直接相關的,大多數的Solaris、Linux和Windows64位系統的jre會返回MMapDirectory,而其餘一些位數的jre,如32位的jre在Windows上會返回SimpleFSDirectory,剩餘的部分會直接使用NIOFSDirectory來存儲索引。那麼這三種方式有什麼不一樣呢?總結以下:ide

一、SimpleFSDirectory:這個類簡單的實現使用RandomAccessFile來完成索引的存儲,讀寫速度通常,併發性不好,在多個線程同時訪問索引時,會形成線程同步,從而大大下降了性能,固然,若是咱們併發性不是很大的話,使用她也是一個不錯的選擇。高併發

二、MMapDirectory:使用內存映射IO的方式來操做索引,在性能上是很是優秀的,讀寫速度很是快,併發性支持通常,固然這種狀況下僅僅侷限於,你的索引的大小小於系統內存的時候,這纔是一個好的選擇,不然,使用不當,將經常會形成內存溢出的異常。性能

三、NIOFSDirectory:使用的是java nio的FileChannel的來操做索引的,讀寫速度快,對併發支持很是有些,由於它利用NIO的特性,避免了同步讀取,因此在高併發的場景下,這個目錄每每是最佳選擇。spa

下面咱們分析FSDirectory的另外一個重要方法sysc()
線程

  protected final Set<String> staleFiles = synchronizedSet(new HashSet<String>()); // Files written, but not yet sync'ed

  @Override
  public void sync(Collection<String> names) throws IOException {
    ensureOpen();
    Set<String> toSync = new HashSet<String>(names);//須要持久化的一些元數據標識
    toSync.retainAll(staleFiles);//此方法會與staleFiles裏面的數據求交集

    for (String name : toSync)
      fsync(name);//把內存中或緩衝區的數據,強制寫到磁盤上,確保數據不會流失

    staleFiles.removeAll(toSync);//在staleFiles中移除已經持久化到磁盤的數據,等待下一次的數據添加
  }

 protected void fsync(String name) throws IOException {
    File fullFile = new File(directory, name);
    boolean success = false;
    int retryCount = 0;
    IOException exc = null;
    while (!success && retryCount < 5) {
      retryCount++;
      RandomAccessFile file = null;
      try {
        try {
          file = new RandomAccessFile(fullFile, "rw");
          file.getFD().sync();//寫入磁盤上
          success = true;
        } finally {
          if (file != null)
            file.close();
        }
      } catch (IOException ioe) {
        if (exc == null)
          exc = ioe;
        try {
          // Pause 5 msec
          Thread.sleep(5);
        } catch (InterruptedException ie) {
          throw new ThreadInterruptedException(ie);
        }
      }
    }
    if (!success)
      // Throw original exception
      throw exc;
  }

其實,sysc這個方法,是從Directory這個頂級父類,繼承過來的,由FSDirectory這個類,對其進行了重寫,這個方法的目的,就是按期根據某些條件,來說咱們內存或緩衝區的數據持久化到磁盤上,以確保咱們已經索引的數據是很是安全的,不會由於一些之外的狀況,如系統崩潰、或忽然宕機、停電的狀況下,對索引結構形成破壞或一些影響。
code

一個簡單地工做流程是這樣的,當咱們進行添加操做是,文件目錄一般會打開一個或幾個特定的文件格式來存儲咱們的數據,好比索引正文的存儲,向量的存儲,位置增量的存儲,不一樣的索引格式負責存儲不一樣的內容,當一些數據添加完畢後,經過某些條件促發持久化操做,好比超出了設置的緩衝區大小,或者超出了默認的Doc數,或者咱們調用了commit方法,這是lucene會調用sysc方法,來把已經添加的數據,存儲到磁盤上,以確保數據的安全存儲,固然這些工做,lucene底層已經給咱們實現好了,咱們並不須要顯式的調用這個方法來完成數據的持久操做,就能絕大多狀況下,安全可靠的完成存儲,而這一切正是sysc發回的關鍵做用。

相關文章
相關標籤/搜索