2014年6月24日 #
Lucene 的 Directory類就像它的意思一樣“目錄”,如“目錄”不存在,第一次啟動被創建,一旦文件被創建,它只能打開閱讀,或刪除。允許讀取和寫入隨機訪問。Java I/O api 不能直接使用,只能通過這個API 。Directory的實現類可以分為文件目錄,內存目錄和目錄的代理類及工具類。具體如下圖所示:
一:文件目錄
SimpleFSDirectory:FSDirectory的簡單實現,并發能力有限,遇到多線程讀同一個文件時會遇到瓶頸,通常用NIOFSDirectory或MMapDirectory代替。
NIOFSDirectory:通過java.nio's FileChannel實行定位讀取,支持多線程讀(默認情況下是線程安全的)。該類僅使用FileChannel進行讀操作,寫操作則是通過FSIndexOutput實現。
注意:NIOFSDirectory 不適用于Windows系統,另外如果一個訪問該類的線程,在IO阻塞時被interrupt或cancel,將會導致底層的文件描述符被關閉,后續的線程再次訪問NIOFSDirectory時將會出現ClosedChannelException異常,此種情況應用SimpleFSDirectory代替。
MMapDirectory:通過內存映射進行讀,通過FSIndexOutput進行寫的FSDirectory實現類。使用該類時要保證用足夠的虛擬地址空間。另外當通過IndexInput的close方法進行關閉時并不會立即關閉底層的文件句柄,只有GC進行資源回收時才會關閉。
為了能適應各個操作系統選擇最佳Directory方案,lucene 提供FSDirectory類的靜態方法open()實現自適應。
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);
}
}
二:內存目錄
RAMDirectory:常駐內存的Directory實現方式。默認通過SingleInstanceLockFactory(單實例鎖工廠)進行鎖的實現。該類不適合大量索引的情況。另外也不適用于多線程的情況。 在索引數據量大的情況下建議使用MMapDirectory代替。RAMDirectory是Directory抽象類在使用內存最為文件存儲的實現類,其主要是將所有的索引文件保存到內存中。這樣可以提高效率。但是如果索引文件過大的話,則會導致內存不足,因此,小型的系統推薦使用,如果大型的,索引文件達到G級別上,推薦使用FSDirectory。
NRTCachingDirectory:是對RAMDirectory的封裝,適用于近乎時時(near-real-time)操作的環境。
三:Direcotry的代理類及工具類
FileSwitchDirectory:文件切換的Directory實現.針對lucene的不同的索引文件使用不同的Directory .借助FileSwitchDirectory整合不同的Directory實現類的優點于一身
比如MMapDirectory,借助內存映射文件方式提高性能,但又要減少內存切換的可能 ,當索引太大的時候,內存映射也需要不斷地切換,這樣優點也可能變缺點,而之前的NIOFSDirectory實現java NIO的方式提高高并發性能,但又因高并發也會導致IO過多的影響,所以這次可以借助FileSwitchDirectory發揮他們兩的優點。
RateLimitedDirectoryWrapper:通過IOContext來限制讀寫速率的Directory封裝類。
CompoundFileDirectory:用于訪問一個組合的數據流。僅適用于讀操作。對于同一段內擴展名不同但文件名相同的所有文件合并到一個統一的.cfs文件和一個對應的.cfe文件內。
.cfs文件由Header,FileData和FileCount組成。.cfe文件由Header,FileCount,FileName,DataOffset,DataLength組成。.cfs文件中存儲著索引的概要信息及組合文件
的數目(FileCount)。.cfe文件存儲文件目錄的條目內容,內容中包括文件數據扇區的起始位置,文件的長度及文件的名稱。
TrackingDirectoryWrapper:Directory的代理類。用于記錄哪些文件被寫入和刪除。
四:Direcotry讀寫對象的類圖
文章轉載過來的!
本機已經安裝了jdk1.6,而比較早期的項目需要依賴jdk1.5,于是同時在本機安裝了jdk1.5和jdk1.6.
安裝jdk1.5前,執行java -version得到
java version "1.6.0_38"
Java(TM) SE Runtime Environment (build 1.6.0_38-b05)
Java HotSpot(TM) 64-Bit Server VM (build 20.13-b02, mixed mode)
安裝完jdk1.5,并修改環境變量JAVA_HOME為D:\devSoftware\jdk1.5.再執行 java -version時,依然顯示:
java version "1.6.0_38"
Java(TM) SE Runtime Environment (build 1.6.0_38-b05)
Java HotSpot(TM) 64-Bit Server VM (build 20.13-b02, mixed mode)
看上去,新的環境變量JAVA_HOME=D:\devSoftware\jdk1.5并沒有生效。 在網上找了很多資料才發現:
在安裝JDK1.6時(本機先安裝jdk1.6再安裝的jdk1.5),自動將java.exe、javaw.exe、javaws.exe三個可執行文件復制到了C:\Windows\System32目錄,由于這個目錄在WINDOWS環境變量中的優先級高于JAVA_HOME設置的環境變量優先級
解決方案:將java.exe,javaw.exe,javaws.exe刪除即可。開啟新的命令行窗口,再執行java -version時,就得到了期望中的結果
java version "1.5.0_17"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_17-b04)
Java HotSpot(TM) 64-Bit Server VM (build 1.5.0_17-b04, mixed mode)
在學lucene 之初看了許多書,都是走馬觀花,沒有項目的驅動下,來一個用例demo感覺也不是很難,“我會了”這是我的第一感覺。
在2013年底公司接到一個項目用到lucene,這是我第一次正真接觸Lucene,代碼比較老3.6版本,不適合新項目的需求(空間查詢)。于是下載了最新版本 4.51,有帶“空間查詢”模塊。各大搜索引擎都沒有找到像樣例子,于是想到了lucene svn的 trunk目錄測試用例中找到了測試例子,開始了一段lucene之旅。
寫數據,創建IndexWriter,通過它的構造函數需要一個索引目錄(Diectory)和索引寫入配置項(InderWriterConfig),直接上代碼:
//設置寫入目錄(好幾種呵呵)
Directory d=FSDirectory.open(new File("D:/luceneTest"));
//設置分詞 StandardAnalyzer(會把句子中的字單個分詞)
Analyzer analyzer= new StandardAnalyzer(Version.LUCENE_45);
//設置索引寫入配置
IndexWriterConfig config=new IndexWriterConfig(Version.LUCENE_45,analyzer);
//設置創建模式
//config.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
IndexWriter indexwriter= new IndexWriter(d,config);
上面四行代碼就創建好了indexwriter,下面把數據填入就好了,寫入有多種方式如下圖:
用 addDocment 舉例代碼如下:
Document doc=new Document();
doc.add(new StringField("id", "1", Store.YES));
doc.add(new StringField("name", "brockhong", Store.YES));
doc.add(new TextField("content", "lucene 文檔第一次寫看著給分吧", Store.YES));
//寫入數據
indexwriter.addDocument(doc);
//提交
indexwriter.commit();
用 Luke 工具查看Text列,這是標準分詞惹的禍哦!寫入成功。
讀數據查詢,創建 IndexSearcher 構造函數設置indexReader ,輸入查詢條件,上面content字段數據設置了分詞,所以必須通過查詢解析類QueryParser設定分詞字段、版本、分詞模式,并通過parse方法得到查詢條件。代碼如下:
//讀數據
//創建 indexReader 這個已過時 IndexReader.open(d),里面的代碼一樣可能為了兼容老版本
IndexReader indexReader = DirectoryReader.open(d);
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//查詢 設置分詞字段
QueryParser queryParser = new QueryParser(Version.LUCENE_45, "content",
new StandardAnalyzer(Version.LUCENE_45));
//or 關系 “給”、“分”
queryParser.setDefaultOperator(QueryParser.OR_OPERATOR);
Query query = queryParser.parse("給分");
TopDocs results = indexSearcher.search(query, 100);
int numTotalHits = results.totalHits;
System.out.println("共 " + numTotalHits + " 完全匹配的文檔");
ScoreDoc[] hits = results.scoreDocs;
for (int i = 0; i < hits.length; i++) {
Document document = indexSearcher.doc(hits[i].doc);
System.out.println("content:" + document.get("content"));
}
使用SAXReader的read(File file)方法時,如果xml文件異常會導致文件被服務器占用不能移動文件,建議不使用read(File file)方法而使用read(FileInputStream fis)等流的方式讀取文件,異常時關閉流,這樣就不會造成流未關閉,文件被鎖的現象了。(在服務器中運行時會鎖住文件,main方法卻不會)。
1、以下方式xml文件異常時會導致文件被鎖
- Document document = null;
- File file = new File(xmlFilePath);
- SAXReader saxReader = new SAXReader();
- try
- {
- document = saxReader.read(file);
- } catch (DocumentException e)
- {
- logger.error("將文件[" + xmlFilePath + "]轉換成Document異常", e);
- }
2、以下方式xml文件異常時不會鎖文件(也可以使用其他的流來讀文件)
- Document document = null;
- FileInputStream fis = null;
- try
- {
- fis = new FileInputStream(xmlFilePath);
- SAXReader reader = new SAXReader();
- document = reader.read(fis);
- }
- catch (Exception e)
- {
- logger.error("將文件[" + xmlFilePath + "]轉換成Document異常", e);
- }
- finally
- {
- if(fis != null)
- {
- try
- {
- fis.close();
- } catch (IOException e)
- {
- logger.error("將文件[" + xmlFilePath + "]轉換成Document,輸入流關閉異常", e);
- }
- }
- }