lucene中的filter器羣組及其緩存大盤點

lucene中的filter其實並不起眼,大家對其對性能的影響也不是很關注,但實際上filter是除了單純搜索以外,其他搜索附加功能的必選組件, 其性能很大程度上會直接影響搜索的性能,之前我一直認爲filter的性能比query高,但事實說明並不完全如此(這裏所說的負荷是指io消耗並不是 cpu),實際上在lucene中充滿着各種io流,也就是說很多東西都無法從根本上保存,這也給緩存帶來了很大難度(這個問題看似簡單,但是在超複雜的 組合查詢下,緩存可能會幾乎無用,原因就是key怎麼把握)

首先來看看filter的接口定義:
public abstract class Filter implements java.io.Serializable {
  public abstract BitSet bits(IndexReader reader) throws IOException;
}

簡單明瞭從reader中知道哪些記錄是可以讀出來的用true false放在bitsets中,然後再用這去和總集合做and操作得到剩餘記錄數,然後再通過query查詢.原理知道了,下面來考慮下它的緩存:
 緩存filter本身,由於他是序列化對象,那麼已經具備了緩存的條件,但是這是一個錯誤,因爲你緩存了這個類,而當你把參數reader拿出來依然會和機器產生io,因此這是極其不恰當的方法,應該緩存它的結果.
在lucene中有這麼幾個和filter有關的類:
CachingWrapperFilter
CachingSpanFilter
RemoteCachingWrapperFilter
FilterManager

其實我想質疑前兩個,爲什麼呢,請看他的源碼:
  protected transient Map cache;

他放置緩存的map居然是transient的,這意味着即使你把它實例在static中這個變量依然會每次要new的,這樣的緩存有意義嗎?我看不出他怎麼緩存的
  /**
   * A transient Filter cache.  To cache Filters even when using {@link org.apache.lucene.search.RemoteSearchable} use
   * {@link org.apache.lucene.search.RemoteCachingWrapperFilter} instead.
   */
上面這句註釋總算明瞭了,呵呵.
那麼其實RemoteCachingWrapperFilter纔是真正的cache類,他的實現藉助於filterManager,這個類是我們平時能理解的那種cache結構
  public BitSet bits(IndexReader reader) throws IOException {
    Filter cachedFilter = FilterManager.getInstance().getFilter(filter);
    return cachedFilter.bits(reader);
  }

但這個還不夠,第一他的性能我心裏沒譜,遇到上萬的訪問怎麼辦?所以還是要用第三方的緩存,我使用的是memcached,這個東西不介紹了,只有一個問題,就是必須要求對象是可序列化的,這個不難理解,要想網絡傳輸只能治麼搞.
我的緩存策略:把最細胞的filter用memached緩存他的結果集,而他的組合fliter用自帶的filtermanager管理就好了.filter怎麼合在一起上次寫過一個,看這裏: http://www.edwardpro.com/post/572/

而 我這樣的道理也是基於filtermanager的key是reader的hashcode,因此他是對應不同的索引的.那麼肯定有朋友問怎麼刷新呢?太 簡單了啊,你的key只要有reader或者search的hashcode就可以了,你一旦更新的源hashcode就變化了.(如果你的search 和reader的hash不是固定的那麼你肯定承受不了100以上的並行訪問,io會高得驚人.)

另外一個技巧,是關於 rangefilter的,這個東西不錯,但是有一點難,在哪裏呢?因爲他的查詢似乎效率不高,因此一定要緩存! 但是key呢?比如我常用的key是timestamp,但是實際中就會發現如果用毫秒的timestamp那麼key幾乎無用,因爲很少相同的,經過改 進,我把時間可以用月做單位,查詢也是如此,如果你的要求高我覺得做到天就ok了,如果你數據再多用到小時肯定也夠了吧,這樣filter的緩存會帶來極 大的性能提升.

那麼實際效果呢,在原來使用時候2臺集羣機(nginx作爲前端代理,後部用resin作爲應用服務器)io平均1.xx 現在加了緩存之後常年保持在0.2左右!性能得到了幾乎5~6倍的提升.而一般查詢一個十萬當量的+ 5個關鍵字 + 3個filter 時間大約是<10ms 非命中時大約是 70~80ms 這個速度如果得到同樣結果的數據庫至少要放大1000倍的時間.

由於我 memcached沒有做集羣是獨立的(事實也應該如此,因爲你兩臺機器的reader的hashcode肯定是不一樣的,放一起也是這樣的結果,這樣也 沒有不好,當一臺機器出現問題或者需要更新代碼可以用時間差來保證負荷平穩過渡,不像以前一臺機器每次重啓都是有點怕怕的,只能找空閒時間纔敢這麼做.

最後要講的query,其實前面我說了半天沒有提到query,query的緩存呢? 其實在lucene中有這麼個類:
QueryFilter
這個類簡單說就是把query變成filter,那幹什麼呢?很簡單啊,這樣任何查詢都會變成filter的,所以所有的緩存都是filter!那麼從緩存中取出來的filterquery怎麼用?

    MatchAllDocsQuery matchAll = new MatchAllDocsQuery();
    result = isearch.search(matchAll, filter, sort);
filter 是用我的合成filter組合的,這樣消耗就更低了,當然不建議無限制增加系統負荷,因爲那樣就幾乎無法重啓了,呵呵.好了基本說到這裏,其實最後我想說 我的核心思想: 任何query都是filter,lucene就是filter查詢,事實是如此的,呵呵.

大家有什麼其他方案也可以討論和交流一下,呵呵.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章