<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    szhswl
    宋針還的個(gè)人空間
    1, 有時(shí)對(duì)于一個(gè)Document來說,有一些Field會(huì)被頻繁地操作,而另一些Field則不會(huì)。這時(shí)可以將頻繁操作的Field和其他Field分開存放,而在搜索時(shí)同時(shí)檢索這兩部分Field而提取出一個(gè)完整的Document。   這要求兩個(gè)索引包含的Document的數(shù)量必須相同。
    在創(chuàng)建索引的時(shí)候,可以同時(shí)創(chuàng)建多個(gè)IndexWriter,將一個(gè)Document根據(jù)需要拆分成多個(gè)包含部分Field的Document,并將這些Document分別添加到不同的索引。
    而在搜索時(shí),則必須借助ParallelReader類來整合。
    Directory dir1=FSDirectory.getDirectory(new File(INDEX_DIR1),false);
    Directory dir2=FSDirectory.getDirectory(new File(INDEX_DIR2),false);
    ParallelReader preader=new ParallelReader();
    preader.add(IndexReader.open(dir1));
    preader.add(IndexReader.open(dir2));

    IndexSearcher searcher=new IndexSearcher(preader);
    之后的操作和一般的搜索相同。

    2, Query的子類. 下面的幾個(gè)搜索在各種不同要求的場合,都會(huì)用到. 需要大家仔細(xì)研讀!

    Query query1 = new TermQuery(new Term(FieldValue, "name1")); // 詞語搜索
    Query query2 = new WildcardQuery(new Term(FieldName, "name*")); // 通配符
    Query query3 = new PrefixQuery(new Term(FieldName, "name1")); // 字段搜索 Field:Keyword,自動(dòng)在結(jié)尾添加 *
    Query query4 = new RangeQuery(new Term(FieldNumber, NumberTools.LongToString(11L)), new Term(FieldNumber, NumberTools.LongToString(13L)), true); // 范圍搜索
    Query query5 = new FilteredQuery(query, filter); // 帶過濾條件的搜索
    Query query6 =new MatchAllDocsQuery(... // 用來匹配所有文檔
    Query query7 = new FuzzyQuery (...模糊搜索
    Query query8 = new RegexQuery (..   正則搜索
    Query query9 = new SpanRegexQuery(...)。 同上, 正則表達(dá)式的查詢:
    Query query9 = new SpanQuery 的子類嵌套其他SpanQuery 增加了 rewrite方法
    Query query10 =new DisjunctionMaxQuery () ..類,提供了針對(duì)某個(gè)短語的最大score。這一點(diǎn)對(duì)多字段的搜索非常有用
    Query query11 = new ConstantScoreQuery 類它包裝了一個(gè) filter produces a score
    equal to the query boost for every matching document.

    BooleanQuery query12= new BooleanQuery();
    booleanQuery.add(termQuery 1, BooleanClause.Occur.SHOULD);
    booleanQuery.add(termQuery 2, BooleanClause.Occur.SHOULD);
      //這個(gè)是為了聯(lián)合多個(gè)查詢而做的Query類. BooleanQuery增加了最小的匹配短語。見:BooleanQuery.setMinimumNumberShouldMatch().


    PhraseQuery
    你可能對(duì)中日關(guān)系比較感興趣,想查找‘中’和‘日’挨得比較近(5個(gè)字的距離內(nèi))的文章,超過這個(gè)距離的不予考慮,你可以:

    PhraseQuery query 13= new PhraseQuery();
    query.setSlop(5);
    query.add(new Term("content ", “中”));
    query.add(new Term(“content”, “日”));

    PhraseQuery對(duì)于短語的順序是不管的,這點(diǎn)在查詢時(shí)除了提高命中率外,也會(huì)對(duì)性能產(chǎn)生很大的影響, 利用SpanNearQuery可以對(duì)短語的順序進(jìn)行控制,提高性能

    BooleanQuery query12=   new SpanNearQuery 可以對(duì)短語的順序進(jìn)行控制,提高性能

    3, 索引文本文件
    如果你想把純文本文件索引起來,而不想自己將它們讀入字符串創(chuàng)建field,你可以用下面的代碼創(chuàng)建field:

    Field field = new Field("content", new FileReader(file));

    這里的file就是該文本文件。該構(gòu)造函數(shù)實(shí)際上是讀去文件內(nèi)容,并對(duì)其進(jìn)行索引,但不存儲(chǔ)


    4, 如何刪除索引
    lucene提供了兩種從索引中刪除document的方法,一種是

    void deleteDocument(int docNum)

    這種方法是根據(jù)document在索引中的編號(hào)來刪除,每個(gè)document加進(jìn)索引后都會(huì)有個(gè)唯一編號(hào),所以根據(jù)編號(hào)刪除是一種精確刪除,但是這個(gè)編號(hào)是索引的內(nèi)部結(jié)構(gòu),一般我們不會(huì)知道某個(gè)文件的編號(hào)到底是幾,所以用處不大。另一種是

    void deleteDocuments(Term term)

    這種方法實(shí)際上是首先根據(jù)參數(shù)term執(zhí)行一個(gè)搜索操作,然后把搜索到的結(jié)果批量刪除了。我們可以通過這個(gè)方法提供一個(gè)嚴(yán)格的查詢條件,達(dá)到刪除指定document的目的。
    下面給出一個(gè)例子:

    Directory dir = FSDirectory.getDirectory(PATH, false);
    IndexReader reader = IndexReader.open(dir);
    Term term = new Term(field, key);
    reader.deleteDocuments(term);
    reader.close();



    5, 如何更新索引
    lucene并沒有提供專門的索引更新方法,我們需要先將相應(yīng)的document刪除,然后再將新的document加入索引。例如:

    Directory dir = FSDirectory.getDirectory(PATH, false);
    IndexReader reader = IndexReader.open(dir);
    Term term = new Term(“title”, “lucene introduction”);
    reader.deleteDocuments(term);
    reader.close();

    IndexWriter writer = new IndexWriter(dir, new StandardAnalyzer(), true);
    Document doc = new Document();
    doc.add(new Field("title", "lucene introduction", Field.Store.YES, Field.Index.TOKENIZED));
    doc.add(new Field("content", "lucene is funny", Field.Store.YES, Field.Index.TOKENIZED));
    writer.addDocument(doc);
    writer.optimize();
    writer.close();


    但是在1.9RC1中說明:
    新增類: org.apache.lucene.index.IndexModifier ,它合并了   IndexWriter 和 IndexReader,好處是我們可以增加和刪除文檔的時(shí)候不同擔(dān)心 synchronisation/locking 的問題了。  

    6, filer類.使用 Filter 對(duì)搜索結(jié)果進(jìn)行過濾,可以獲得更小范圍內(nèi)更精確的結(jié)果。 有人說: 注意它執(zhí)行的是預(yù)處理,而不是對(duì)查詢結(jié)果進(jìn)行過濾,所以使用filter的代價(jià)是很大的,它可能會(huì)使一次查詢耗時(shí)提高一百倍


    ISOLatin1AccentFilter ,用 ISO Latin 1 字符集中的unaccented類字符替代 accented 類字符
    DateFilter   日期過濾器
    RangeFileter ,比 DateFilter 更加通用,實(shí)用
    LengthFilter 類, 已經(jīng)從 contrib 放到了 core 代碼里。從 stream 中去掉太長和太短的單詞   StopFilter 類, 增加了對(duì)處理stop words 的忽略大小寫處理


    7,本條是一個(gè)使用過濾的說明:

    過濾

    使用 Filter 對(duì)搜索結(jié)果進(jìn)行過濾,可以獲得更小范圍內(nèi)更精確的結(jié)果。

    舉個(gè)例子,我們搜索上架時(shí)間在 2005-10-1 到 2005-10-30 之間的商品。
    對(duì)于日期時(shí)間,我們需要轉(zhuǎn)換一下才能添加到索引庫,同時(shí)還必須是索引字段。
    // index
    document.Add(FieldDate, DateField.DateToString(date), Field.Store.YES, Field.Index.UN_TOKENIZED);

    //...

    // search
    Filter filter = new DateFilter(FieldDate, DateTime.Parse("2005-10-1"), DateTime.Parse("2005-10-30"));
    Hits hits = searcher.Search(query, filter);

    除了日期時(shí)間,還可以使用整數(shù)。比如搜索價(jià)格在 100 ~ 200 之間的商品。
    Lucene.Net NumberTools 對(duì)于數(shù)字進(jìn)行了補(bǔ)位處理,如果需要使用浮點(diǎn)數(shù)可以自己參考源碼進(jìn)行。
    // index
    document.Add(new Field(FieldNumber, NumberTools.LongToString((long)price), Field.Store.YES, Field.Index.UN_TOKENIZED));

    //...

    // search
    Filter filter = new RangeFilter(FieldNumber, NumberTools.LongToString(100L), NumberTools.LongToString(200L), true, true);
    Hits hits = searcher.Search(query, filter);

    使用 Query 作為過濾條件。
    QueryFilter filter = new QueryFilter(QueryParser.Parse("name2", FieldValue, analyzer));

    我們還可以使用 FilteredQuery 進(jìn)行多條件過濾。

    Filter filter = new DateFilter(FieldDate, DateTime.Parse("2005-10-10"), DateTime.Parse("2005-10-15"));
    Filter filter2 = new RangeFilter(FieldNumber, NumberTools.LongToString(11L), NumberTools.LongToString(13L), true, true);

    Query query = QueryParser.Parse("name*", FieldName, analyzer);
    query = new FilteredQuery(query, filter);
    query = new FilteredQuery(query, filter2);

    IndexSearcher searcher = new IndexSearcher(reader);
    Hits hits = searcher.Search(query);



    8, Sort
    有時(shí)你想要一個(gè)排好序的結(jié)果集,就像SQL語句的“order by”,lucene能做到:通過Sort。
    Sort sort = new Sort(“time”); //相當(dāng)于SQL的“order by time”
    Sort sort = new Sort(“time”, true); // 相當(dāng)于SQL的“order by time desc”
    下面是一個(gè)完整的例子:

    Directory dir = FSDirectory.getDirectory(PATH, false);
    IndexSearcher is = new IndexSearcher(dir);
    QueryParser parser = new QueryParser("content", new StandardAnalyzer());
    Query query = parser.parse("title:lucene content:lucene";
    RangeFilter filter = new RangeFilter("time", "20060101", "20060230", true, true);
    Sort sort = new Sort(“time”);
    Hits hits = is.search(query, filter, sort);
    for (int i = 0; i < hits.length(); i++)
    {
    Document doc = hits.doc(i);
    System.out.println(doc.get("title");
    }
    is.close();

    9,   性能優(yōu)化
    一直到這里,我們還是在討論怎么樣使lucene跑起來,完成指定任務(wù)。利用前面說的也確實(shí)能完成大部分功能。但是測試表明lucene的性能并不是很好,在大數(shù)據(jù)量大并發(fā)的條件下甚至?xí)邪敕昼姺祷氐那闆r。另外大數(shù)據(jù)量的數(shù)據(jù)初始化建立索引也是一個(gè)十分耗時(shí)的過程。那么如何提高lucene的性能呢?下面從優(yōu)化創(chuàng)建索引性能和優(yōu)化搜索性能兩方面介紹。

    9.1 優(yōu)化創(chuàng)建索引性能
    這方面的優(yōu)化途徑比較有限,IndexWriter提供了一些接口可以控制建立索引的操作,另外我們可以先將索引寫入RAMDirectory,再批量寫入FSDirectory,不管怎樣,目的都是盡量少的文件IO,因?yàn)閯?chuàng)建索引的最大瓶頸在于磁盤IO。另外選擇一個(gè)較好的分析器也能提高一些性能。

    9.1.1 通過設(shè)置IndexWriter的參數(shù)優(yōu)化索引建立
    setMaxBufferedDocs(int maxBufferedDocs)
    控制寫入一個(gè)新的segment前內(nèi)存中保存的document的數(shù)目,設(shè)置較大的數(shù)目可以加快建索引速度,默認(rèn)為10。
    setMaxMergeDocs(int maxMergeDocs)
    控制一個(gè)segment中可以保存的最大document數(shù)目,值較小有利于追加索引的速度,默認(rèn)Integer.MAX_VALUE,無需修改。
    setMergeFactor(int mergeFactor)
    控制多個(gè)segment合并的頻率,值較大時(shí)建立索引速度較快,默認(rèn)是10,可以在建立索引時(shí)設(shè)置為100。

    9.1.2 通過RAMDirectory緩寫提高性能
    我們可以先把索引寫入RAMDirectory,達(dá)到一定數(shù)量時(shí)再批量寫進(jìn)FSDirectory,減少磁盤IO次數(shù)。

    FSDirectory fsDir = FSDirectory.getDirectory("/data/index", true);
    RAMDirectory ramDir = new RAMDirectory();
    IndexWriter fsWriter = new IndexWriter(fsDir, new StandardAnalyzer(), true);
    IndexWriter ramWriter = new IndexWriter(ramDir, new StandardAnalyzer(), true);
    while (there are documents to index)
    {
    ... create Document ...
    ramWriter.addDocument(doc);
    if (condition for flushing memory to disk has been met)
    {
    fsWriter.addIndexes(new Directory[] { ramDir });
    ramWriter.close();
    ramWriter = new IndexWriter(ramDir, new StandardAnalyzer(), true);
    }
    }

    9.1.3 選擇較好的分析器
    這個(gè)優(yōu)化主要是對(duì)磁盤空間的優(yōu)化,可以將索引文件減小將近一半,相同測試數(shù)據(jù)下由600M減少到380M。但是對(duì)時(shí)間并沒有什么幫助,甚至?xí)枰L時(shí)間,因?yàn)檩^好的分析器需要匹配詞庫,會(huì)消耗更多cpu,測試數(shù)據(jù)用StandardAnalyzer耗時(shí)133分鐘;用MMAnalyzer耗時(shí)150分鐘。

    9.2 優(yōu)化搜索性能
    雖然建立索引的操作非常耗時(shí),但是那畢竟只在最初創(chuàng)建時(shí)才需要,平時(shí)只是少量的維護(hù)操作,更何況這些可以放到一個(gè)后臺(tái)進(jìn)程處理,并不影響用戶搜索。我們創(chuàng)建索引的目的就是給用戶搜索,所以搜索的性能才是我們最關(guān)心的。下面就來探討一下如何提高搜索性能。

    9.2.1 將索引放入內(nèi)存
    這是一個(gè)最直觀的想法,因?yàn)閮?nèi)存比磁盤快很多。Lucene提供了RAMDirectory可以在內(nèi)存中容納索引:

    Directory fsDir = FSDirectory.getDirectory(“/data/index/”, false);
    Directory ramDir = new RAMDirectory(fsDir);
    Searcher searcher = new IndexSearcher(ramDir);

    但是實(shí)踐證明RAMDirectory和FSDirectory速度差不多,當(dāng)數(shù)據(jù)量很小時(shí)兩者都非常快,當(dāng)數(shù)據(jù)量較大時(shí)(索引文件400M)RAMDirectory甚至比FSDirectory還要慢一點(diǎn),這確實(shí)讓人出乎意料。
    而且lucene的搜索非常耗內(nèi)存,即使將400M的索引文件載入內(nèi)存,在運(yùn)行一段時(shí)間后都會(huì)out of memory,所以個(gè)人認(rèn)為載入內(nèi)存的作用并不大。

    9.2.2 優(yōu)化時(shí)間范圍限制
    既然載入內(nèi)存并不能提高效率,一定有其它瓶頸,經(jīng)過測試發(fā)現(xiàn)最大的瓶頸居然是時(shí)間范圍限制,那么我們可以怎樣使時(shí)間范圍限制的代價(jià)最小呢?
    當(dāng)需要搜索指定時(shí)間范圍內(nèi)的結(jié)果時(shí),可以:
    1、用RangeQuery,設(shè)置范圍,但是RangeQuery的實(shí)現(xiàn)實(shí)際上是將時(shí)間范圍內(nèi)的時(shí)間點(diǎn)展開,組成一個(gè)個(gè)BooleanClause加入到BooleanQuery中查詢, 因此時(shí)間范圍不可能設(shè)置太大,經(jīng)測試,范圍超過一個(gè)月就會(huì)拋BooleanQuery.TooManyClauses,可以通過設(shè)置BooleanQuery.setMaxClauseCount(int maxClauseCount)擴(kuò)大,但是擴(kuò)大也是有限的,并且隨著maxClauseCount擴(kuò)大,占用內(nèi)存也擴(kuò)大
    2、用RangeFilter代替RangeQuery,經(jīng)測試速度不會(huì)比RangeQuery慢,但是仍然有性能瓶頸,查詢的90%以上時(shí)間耗費(fèi)在RangeFilter,研究其源碼發(fā)現(xiàn)RangeFilter實(shí)際上是首先遍歷所有索引,生成一個(gè)BitSet,標(biāo)記每個(gè)document,在時(shí)間范圍內(nèi)的標(biāo)記為true,不在的標(biāo)記為false,然后將結(jié)果傳遞給Searcher查找,這是十分耗時(shí)的。
    3、進(jìn)一步提高性能,這個(gè)又有兩個(gè)思路:
    a、緩存Filter結(jié)果。既然RangeFilter的執(zhí)行是在搜索之前,那么它的輸入都是一定的,就是IndexReader,而IndexReader是由Directory決定的,所以可以認(rèn)為RangeFilter的結(jié)果是由范圍的上下限決定的,也就是由具體的RangeFilter對(duì)象決定,所以我們只要以RangeFilter對(duì)象為鍵,將filter結(jié)果BitSet緩存起來即可。lucene API已經(jīng)提供了一個(gè)CachingWrapperFilter類封裝了Filter及其結(jié)果,所以具體實(shí)施起來我們可以cache CachingWrapperFilter對(duì)象,需要注意的是,不要被CachingWrapperFilter的名字及其說明誤導(dǎo),CachingWrapperFilter看起來是有緩存功能,但的緩存是針對(duì)同一個(gè)filter的,也就是在你用同一個(gè)filter過濾不同IndexReader時(shí),它可以幫你緩存不同IndexReader的結(jié)果,而我們的需求恰恰相反,我們是用不同filter過濾同一個(gè)IndexReader,所以只能把它作為一個(gè)封裝類。
    b、降低時(shí)間精度。研究Filter的工作原理可以看出,它每次工作都是遍歷整個(gè)索引的,所以時(shí)間粒度越大,對(duì)比越快,搜索時(shí)間越短,在不影響功能的情況下,時(shí)間精度越低越好,有時(shí)甚至犧牲一點(diǎn)精度也值得,當(dāng)然最好的情況是根本不作時(shí)間限制。
    下面針對(duì)上面的兩個(gè)思路演示一下優(yōu)化結(jié)果(都采用800線程隨機(jī)關(guān)鍵詞隨即時(shí)間范圍):
    第一組,時(shí)間精度為秒:
    方式 直接用RangeFilter 使用cache 不用filter
    平均每個(gè)線程耗時(shí) 10s 1s 300ms

    第二組,時(shí)間精度為天
    方式 直接用RangeFilter 使用cache 不用filter
    平均每個(gè)線程耗時(shí) 900ms 360ms 300ms

    由以上數(shù)據(jù)可以得出結(jié)論:
    1、 盡量降低時(shí)間精度,將精度由秒換成天帶來的性能提高甚至比使用cache還好,最好不使用filter。
    2、 在不能降低時(shí)間精度的情況下,使用cache能帶了10倍左右的性能提高。

    9.2.3 使用更好的分析器
    這個(gè)跟創(chuàng)建索引優(yōu)化道理差不多,索引文件小了搜索自然會(huì)加快。當(dāng)然這個(gè)提高也是有限的。較好的分析器相對(duì)于最差的分析器對(duì)性能的提升在20%以下。

    10 一些經(jīng)驗(yàn)

    10.1關(guān)鍵詞區(qū)分大小寫
    or AND TO等關(guān)鍵詞是區(qū)分大小寫的,lucene只認(rèn)大寫的,小寫的當(dāng)做普通單詞。

    10.2 讀寫互斥性
    同一時(shí)刻只能有一個(gè)對(duì)索引的寫操作,在寫的同時(shí)可以進(jìn)行搜索

    10.3 文件鎖
    在寫索引的過程中強(qiáng)行退出將在tmp目錄留下一個(gè)lock文件,使以后的寫操作無法進(jìn)行,可以將其手工刪除

    10.4 時(shí)間格式
    lucene只支持一種時(shí)間格式y(tǒng)yMMddHHmmss,所以你傳一個(gè)yy-MM-dd HH:mm:ss的時(shí)間給lucene它是不會(huì)當(dāng)作時(shí)間來處理的

    10.5 設(shè)置boost
    有些時(shí)候在搜索時(shí)某個(gè)字段的權(quán)重需要大一些,例如你可能認(rèn)為標(biāo)題中出現(xiàn)關(guān)鍵詞的文章比正文中出現(xiàn)關(guān)鍵詞的文章更有價(jià)值,你可以把標(biāo)題的boost設(shè)置的更大,那么搜索結(jié)果會(huì)優(yōu)先顯示標(biāo)題中出現(xiàn)關(guān)鍵詞的文章(沒有使用排序的前題下)。使用方法:
    Field. setBoost(float boost);默認(rèn)值是1.0,也就是說要增加權(quán)重的需要設(shè)置得比1大。

    上面這篇關(guān)于性能的講解是很深刻. 請(qǐng)學(xué)習(xí).

    本文轉(zhuǎn)自:http://zhangxinzhou.blog.ccidnet.com/blog-htm-do-showone-uid-36421-type-blog-itemid-213713.html


    ---------------------------------------------------------------------------------------------------------------------------------
    說人之短,乃護(hù)己之短。夸己之長,乃忌人之長。皆由存心不厚,識(shí)量太狹耳。能去此弊,可以進(jìn)德,可以遠(yuǎn)怨。
    http://www.tkk7.com/szhswl
    ------------------------------------------------------------------------------------------------------ ----------------- ---------
    posted on 2007-12-17 19:36 宋針還 閱讀(402) 評(píng)論(0)  編輯  收藏 所屬分類: 搜索引擎
    主站蜘蛛池模板: 亚洲成AV人片一区二区| 精品免费国产一区二区三区| 国产亚洲AV手机在线观看| 看一级毛片免费观看视频| 内射无码专区久久亚洲| 蜜臀亚洲AV无码精品国产午夜.| 成年女人免费v片| 国产亚洲欧美日韩亚洲中文色| 免费看大美女大黄大色| 在线看亚洲十八禁网站| 亚洲AV成人精品日韩一区18p| 日韩一区二区三区免费播放| 怡红院亚洲怡红院首页| 日韩视频免费在线观看| 亚洲国产精品久久人人爱| 好吊妞788免费视频播放| 含羞草国产亚洲精品岁国产精品| 亚洲精品tv久久久久久久久久| 一个人晚上在线观看的免费视频| 日本红怡院亚洲红怡院最新| 最近2019中文字幕免费大全5| 亚洲AV无码一区二区三区在线| 国产精品色午夜视频免费看| 91精品成人免费国产| 亚洲精品福利在线观看| 午夜dj免费在线观看| 青青操免费在线视频| 亚洲香蕉久久一区二区| 亚洲国产精品碰碰| 中文字幕在线免费| 理论片在线观看免费| 亚洲系列国产精品制服丝袜第| 永久免费看bbb| 182tv免费视频在线观看| 亚洲视频一区二区三区四区| 亚洲天堂中文字幕在线| 亚洲视频免费观看| 日本视频免费观看| 亚洲中文无码线在线观看| 精品亚洲视频在线观看| 成年在线观看网站免费|